Reputation:
I have code like this. Is there a way to make it easier to write and maintain? Using C# .NET 3.5.
string header(string title)
{
StringWriter s = new StringWriter();
s.WriteLine("{0}","<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">");
s.WriteLine("{0}", "<html>");
s.WriteLine("<title>{0}</title>", title);
s.WriteLine("{0}","<link rel=\"stylesheet\" type=\"text/css\" href=\"style.css\">");
s.WriteLine("{0}", "</head>");
s.WriteLine("{0}", "<body>");
s.WriteLine("{0}", "");
}
I could also just write:
s.WriteLine("{0}", @"blah blah
many
new
lines
blah UHY#$&_#$_*@Y KSDSD<>\t\t\t\t\t\tt\t\t\\\t\t\t\t\\\h\th'\h't\th
hi
done");
It will work, but I need to replace all "
with ""
.
Upvotes: 52
Views: 182382
Reputation: 1459
I was looking for something that looked like jquery for generating dom in C# (I don't need to parse). Unfortunately no luck in finding a lightweight solution so I created this simple class that is inherited from System.Xml.Linq.XElement. The key feature is that you can chain the operator like when using jquery in javascript so it's more fluent. It's not fully featured but it does what I need and if there is interest I can start a git.
public class DomElement : XElement
{
public DomElement(string name) : base(name)
{
}
public DomElement(string name, string value) : base(name, value)
{
}
public DomElement Css(string style, string value)
{
style = style.Trim();
value = value.Trim();
var existingStyles = new Dictionary<string, string>();
var xstyle = this.Attribute("style");
if (xstyle != null)
{
foreach (var s in xstyle.Value.Split(';'))
{
var keyValue = s.Split(':');
existingStyles.Add(keyValue[0], keyValue.Length < 2 ? null : keyValue[1]);
}
}
if (existingStyles.ContainsKey(style))
{
existingStyles[style] = value;
}
else
{
existingStyles.Add(style, value);
}
var styleString = string.Join(";", existingStyles.Select(s => $"{s.Key}:{s.Value}"));
this.SetAttributeValue("style", styleString);
return this;
}
public DomElement AddClass(string cssClass)
{
var existingClasses = new List<string>();
var xclass = this.Attribute("class");
if (xclass != null)
{
existingClasses.AddRange(xclass.Value.Split());
}
var addNewClasses = cssClass.Split().Where(e => !existingClasses.Contains(e));
existingClasses.AddRange(addNewClasses);
this.SetAttributeValue("class", string.Join(" ", existingClasses));
return this;
}
public DomElement Text(string text)
{
this.Value = text;
return this;
}
public DomElement Append(string text)
{
this.Add(text);
return this;
}
public DomElement Append(DomElement child)
{
this.Add(child);
return this;
}
}
Sample:
void Main()
{
var html = new DomElement("html")
.Append(new DomElement("head"))
.Append(new DomElement("body")
.Append(new DomElement("p")
.Append("This paragraph contains")
.Append(new DomElement("b", "bold"))
.Append(" text.")
)
.Append(new DomElement("p").Text("This paragraph has just plain text"))
)
;
html.ToString().Dump();
var table = new DomElement("table").AddClass("table table-sm").AddClass("table-striped")
.Append(new DomElement("thead")
.Append(new DomElement("tr")
.Append(new DomElement("td").Css("padding-left", "15px").Css("color", "red").Css("color", "blue")
.AddClass("from-now")
.Append(new DomElement("div").Text("Hi there"))
.Append(new DomElement("div").Text("Hey there"))
.Append(new DomElement("div", "Yo there"))
)
)
)
;
table.ToString().Dump();
}
output from above code:
<html>
<head />
<body>
<p>This paragraph contains<b>bold</b> text.</p>
<p>This paragraph has just plain text</p>
</body>
</html>
<table class="table table-sm table-striped">
<thead>
<tr>
<td style="padding-left:15px;color:blue" class="from-now">
<div>Hi there</div>
<div>Hey there</div>
<div>Yo there</div>
</td>
</tr>
</thead>
</table>
Upvotes: 0
Reputation: 116448
You're probably better off using an HtmlTextWriter
or an XMLWriter
than a plain StringWriter
. They will take care of escaping for you, as well as making sure the document is well-formed.
This page shows the basics of using the HtmlTextWriter
class, the gist of which being:
StringWriter stringWriter = new StringWriter();
using (HtmlTextWriter writer = new HtmlTextWriter(stringWriter))
{
writer.AddAttribute(HtmlTextWriterAttribute.Class, classValue);
writer.RenderBeginTag(HtmlTextWriterTag.Div); // Begin #1
writer.AddAttribute(HtmlTextWriterAttribute.Href, urlValue);
writer.RenderBeginTag(HtmlTextWriterTag.A); // Begin #2
writer.AddAttribute(HtmlTextWriterAttribute.Src, imageValue);
writer.AddAttribute(HtmlTextWriterAttribute.Width, "60");
writer.AddAttribute(HtmlTextWriterAttribute.Height, "60");
writer.AddAttribute(HtmlTextWriterAttribute.Alt, "");
writer.RenderBeginTag(HtmlTextWriterTag.Img); // Begin #3
writer.RenderEndTag(); // End #3
writer.Write(word);
writer.RenderEndTag(); // End #2
writer.RenderEndTag(); // End #1
}
// Return the result.
return stringWriter.ToString();
Upvotes: 91
Reputation: 37885
With the introduction of Razor in ASP.net MVC, the best way to write HTML in C# is with the Razor Engine.
string templatePath = $@"{Directory.GetCurrentDirectory()}\EmailTemplates";
IRazorLightEngine engine = EngineFactory.CreatePhysical(templatePath);
var model = new Notification
{
Name = "Jone",
Title = "Test Email",
Content = "This is a test"
};
string result = engine.Parse("template.cshtml", model);
Template:
<h2>Dear @Model.Name, you have a notification.</h2>
<h1>@Model.Title</h1>
<p>@Model.Content</p>
<p>Date:@DateTime.Now</p>
For a complete sample, see here
Upvotes: 3
Reputation: 21
HSharp is a library used to analyse markup language like HTML easily and fastly.
Install: PM> Install-Package Obisoft.HSharp
var Document = new HDoc(DocumentOptions.BasicHTML);
Document["html"]["body"].AddChild("div");
Document["html"]["body"]["div"].AddChild("a", new HProp("href", "/#"));
Document["html"]["body"]["div"].AddChild("table");
Document["html"]["body"]["div"]["table"].AddChildren(
new HTag("tr"),
new HTag("tr", "SomeText"),
new HTag("tr", new HTag("td")));
var Result = Document.GenerateHTML();
Console.WriteLine(Result);
and output:
<html>
<head>
<meta charset="utf-8"></meta><title>
Example </title>
</head>
<body>
<div>
<a href="/#"></a><table>
<tr></tr><tr>
SomeText </tr>
<tr>
<td></td></tr>
</table>
</div>
</body>
</html>
Upvotes: 0
Reputation: 69242
You could use System.Xml.Linq
objects. They were totally redesigned from the old System.Xml
days which made constructing XML from scratch really annoying.
Other than the doctype I guess, you could easily do something like:
var html = new XElement("html",
new XElement("head",
new XElement("title", "My Page")
),
new XElement("body",
"this is some text"
)
);
Upvotes: 7
Reputation: 13643
Use an XDocument
to create the DOM, then write it out using an XmlWriter
. This will give you a wonderfully concise and readable notation as well as nicely formatted output.
Take this sample program:
using System.Xml;
using System.Xml.Linq;
class Program {
static void Main() {
var xDocument = new XDocument(
new XDocumentType("html", null, null, null),
new XElement("html",
new XElement("head"),
new XElement("body",
new XElement("p",
"This paragraph contains ", new XElement("b", "bold"), " text."
),
new XElement("p",
"This paragraph has just plain text."
)
)
)
);
var settings = new XmlWriterSettings {
OmitXmlDeclaration = true, Indent = true, IndentChars = "\t"
};
using (var writer = XmlWriter.Create(@"C:\Users\wolf\Desktop\test.html", settings)) {
xDocument.WriteTo(writer);
}
}
}
This generates the following output:
<!DOCTYPE html >
<html>
<head />
<body>
<p>This paragraph contains <b>bold</b> text.</p>
<p>This paragraph has just plain text.</p>
</body>
</html>
Upvotes: 17
Reputation: 296
You can use T4 Templates for generating Html (or any) from your code. see this: http://msdn.microsoft.com/en-us/library/ee844259.aspx
Upvotes: 5
Reputation: 13513
I wrote these classes which served me well. It's simple yet pragmatic.
public class HtmlAttribute
{
public string Name { get; set; }
public string Value { get; set; }
public HtmlAttribute(string name) : this(name, null) { }
public HtmlAttribute(
string name,
string @value)
{
this.Name = name;
this.Value = @value;
}
public override string ToString()
{
if (string.IsNullOrEmpty(this.Value))
return this.Name;
if (this.Value.Contains('"'))
return string.Format("{0}='{1}'", this.Name, this.Value);
return string.Format("{0}=\"{1}\"", this.Name, this.Value);
}
}
public class HtmlElement
{
protected List<HtmlAttribute> Attributes { get; set; }
protected List<object> Childs { get; set; }
public string Name { get; set; }
protected HtmlElement Parent { get; set; }
public HtmlElement() : this(null) { }
public HtmlElement(string name, params object[] childs)
{
this.Name = name;
this.Attributes = new List<HtmlAttribute>();
this.Childs = new List<object>();
if (childs != null && childs.Length > 0)
{
foreach (var c in childs)
{
Add(c);
}
}
}
public void Add(object o)
{
var a = o as HtmlAttribute;
if (a != null)
this.Attributes.Add(a);
else
{
var h = o as HtmlElement;
if (h != null && !string.IsNullOrEmpty(this.Name))
{
h.Parent = this;
this.Childs.Add(h);
}
else
this.Childs.Add(o);
}
}
public override string ToString()
{
var result = new StringBuilder();
if (!string.IsNullOrEmpty(this.Name))
{
result.Append(string.Format("<{0}", this.Name));
if (this.Attributes.Count > 0)
{
result.Append(" ");
foreach (var attr in this.Attributes)
{
result.Append(attr.ToString());
result.Append(" ");
}
result = new StringBuilder(result.ToString().TrimEnd(' '));
}
if (this.Childs.Count == 0)
{
result.Append(" />");
}
else
{
result.AppendLine(">");
foreach (var c in this.Childs)
{
var cParts = c.ToString().Split('\n');
foreach (var p in cParts)
{
result.AppendLine(string.Format("{0}", p));
}
}
result.Append(string.Format("</{0}>", this.Name));
}
}
else
{
foreach (var c in this.Childs)
{
var cParts = c.ToString().Split('\n');
foreach (var p in cParts)
{
result.AppendLine(string.Format("{0}", p));
}
}
}
var head = GetHeading(this);
var ps = result.ToString().Split('\n');
return string.Join("\r\n", (from p in ps select head + p.TrimEnd('\r')).ToArray());
}
string GetHeading(HtmlElement h)
{
if (h.Parent != null)
return " ";
else
return string.Empty;
}
}
Upvotes: 0
Reputation: 2387
You could use some third party open-source libraries to generated strong typed verified (X)HTML, such as CityLizard Framework or Sharp DOM.
Update For example
html
[head
[title["Title of the page"]]
[meta_(
content: "text/html;charset=UTF-8",
http_equiv: "Content-Type")
]
[link_(href: "css/style.css", rel: "stylesheet", type: "text/css")]
[script_(type: "text/javascript", src: "/JavaScript/jquery-1.4.2.min.js")]
]
[body
[div
[h1["Test Form to Test"]]
[form_(action: "post", id: "Form1")
[div
[label["Parameter"]]
[input_(type: "text", value: "Enter value")]
[input_(type: "submit", value: "Submit!")]
]
]
[div
[p["Textual description of the footer"]]
[a_(href: "http://google.com/")
[span["You can find us here"]]
]
[div["Another nested container"]]
]
]
];
Upvotes: 2
Reputation: 1046
I know you asked about C#, but if you're willing to use any .Net language then I highly recommend Visual Basic for this exact problem. Visual Basic has a feature called XML Literals that will allow you to write code like this.
Module Module1
Sub Main()
Dim myTitle = "Hello HTML"
Dim myHTML = <html>
<head>
<title><%= myTitle %></title>
</head>
<body>
<h1>Welcome</h1>
<table>
<tr><th>ID</th><th>Name</th></tr>
<tr><td>1</td><td>CouldBeAVariable</td></tr>
</table>
</body>
</html>
Console.WriteLine(myHTML)
End Sub
End Module
This allows you to write straight HTML with expression holes in the old ASP style and makes your code super readable. Unfortunately this feature is not in C#, but you could write a single module in VB and add it as a reference to your C# project.
Writing in Visual Studio also allows proper indentation for most XML Literals and expression wholes. Indentation for the expression holes is better in VS2010.
Upvotes: 14
Reputation: 78850
You can use ASP.NET to generate your HTML outside the context of web pages. Here's an article that shows how it can be done.
Upvotes: 3
Reputation: 1795
This is not a generic solution, however, if your pupose is to have or maintain email templates then System.Web has a built-in class called MailDefinition. This class is used by the ASP.NET membership controls to create HTML emails.
Does the same kind of 'string replace' things as mentioned above, but packs it all into a MailMessage for you.
Here is an example from MSDN:
ListDictionary replacements = new ListDictionary();
replacements.Add("<%To%>",sourceTo.Text);
replacements.Add("<%From%>", md.From);
System.Net.Mail.MailMessage fileMsg;
fileMsg = md.CreateMailMessage(toAddresses, replacements, emailTemplate, this);
return fileMsg;
Upvotes: 1
Reputation: 103387
If you're looking to create an HTML document similar to how you would create an XML document in C#, you could try Microsoft's open source library, the Html Agility Pack.
It provides an HtmlDocument object that has a very similar API to the System.Xml.XmlDocument
class.
Upvotes: 3
Reputation: 6385
It really depends what you are going for, and specifically, what kind of performance you really need to offer.
I've seen admirable solutions for strongly-typed HTML development (complete control models, be it ASP.NET Web Controls, or similar to it) that just add amazing complexity to a project. In other situations, it is perfect.
In order of preference in the C# world,
Upvotes: 0
Reputation: 5348
You could write your own classes with its Render method, and another attributes, to avoid a great mess if you use it a lot, and then use the HTMLWriter or the xmlwriter as well.
This logic is used in the asp.net pages, you can inherit from webControl and override the render method, wich is great if you are developing server-side controls.
This could be a good example.
Regards
Upvotes: 0
Reputation: 754565
The most straight forward way is to use an XmlWriter object. This can be used to produce valid HTML and will take care of all of the nasty escape sequences for you.
Upvotes: 5
Reputation: 38956
When I deal with this problem in other languages I go for a separation of code and HTML. Something like:
1.) Create a HTML template. use [varname]
placeholders to mark replaced/inserted content.
2.) Fill your template variables from an array or structure/mapping/dictionary
Write( FillTemplate(myHTMLTemplate, myVariables) ) # pseudo-code
Upvotes: 25
Reputation: 91432
return string.Format(@"<!DOCTYPE HTML PUBLIC ""-//W3C//DTD HTML 4.01//EN"" ""http://www.w3.org/TR/html4/strict.dtd"">
<html>
<title>{0}</title>
<link rel=""stylesheet"" type=""text/css"" href=""style.css"">
</head>
<body>
", title);
Upvotes: 7