(See history for list of changes.)
Introduction
This is a library of 100% managed code that draws beautifully formatted HTML. It comes along with three WinForms controls:
- HtmlPanel
- HtmlLabel
- HtmlTooltip
And a static method ready to draw HTML:
Note: The drawing engine is based on the CSS Level 2 specification.
Background
For years, I have been planning for a project like this. I prepared myself quite well. I went through the entire CSS Level 2 specification along with the HTML 4.01 specification.
One of the most interesting things I found is this: Drawing HTML is no more than laying out a bunch of boxes with borders margins and paddings. Once you overpass this paradigm, everything else is to help the code actually place the boxes on the right place, and then paint the string each box contains.
Imagine the power that drawing full-rich-formatted HTML on your controls can give to your applications. Use bold when you need it, italics on every message, and borders and fonts as you may like or need everywhere on the desktop application. One of the first projects where I will use it is on the tooltips of my Ribbon Project.
Although I have not tested it on mono yet, there should be no problem at all, since all of the code in the library is managed code and the methods it uses to paint are quite basic. It draws lines, rectangles, curves and text.
For now, the render looks really nice. Sometimes it can fool you to think you're using a real Web browser, trust me, download the demo, it is just an EXE and a DLL.
Using the Code
The library locates the code under the System.Drawing.Html namespace. The controls that render HTML are under the System.Windows.Forms namespace.
The renderer follows the CSS Box Model. Box model is nothing but a tree of boxes, just as the tree of HTML, each of these boxes is represented by a very used class called CssBox. The start node is represented by the class InitialContainer.
All the known CSS properties apply to each of these boxes. Each box may contain any number of child boxes and just one parent. The only box that has no parent at all is the so called Initial Container.
A typical use of an Initial Container to draw HTML would look like this:
InitialContainer c = new InitialContainer("<html>");
c.SetBounds(ClientRectangle);
c.MeasureBounds(graphics);
c.Paint(graphics);

First a label, then a panel and at last a ToolTip, all of which support HTML rendering.
You may never use it, since I provided controls and methods that create this object for you.
HtmlPanel
A panel that is ready to accept HTML code via its Text property. Its full name is System.Windows.Forms.HtmlPanel.
The only properties you need to know are:
- AutoScroll. Activates/Deactivates the auto-scroll capabilities as you know. It is set to
true by default.
- Text. Gets/Sets the HTML source.
The panel will update the bounds of the elements as you scroll or resize the control.
HtmlLabel
A label that is ready to accept HTML code via its Text property. Its full name is System.Windows.Forms.HtmlLabel.
The only properties you need to know are:
- AutoScroll. Activates/Deactivates the auto-scroll capabilities as you know. It is set to
true by default.
- AutoSize. Sets the size of the label automatically if activated.
- Text. Gets/Sets the HTML source.
Some interesting things:
- The label will update the bounds of the elements as you scroll or resize the control.
- The label can be transparent.
- The panel has better performance than the label.
HtmlToolTip
Works exactly like the ToolTip you already know, with the little difference that this tooltip will render HTML on it. It's full name is System.Windows.Forms.HtmlToolTip.
There are no properties here to learn. Use it just the way you use the ToolTip that comes with the framework. Internally, it just handles the OwnerDraw event.
Some Features of my Own
I took the liberty of adding a couple of features:
- Background gradients
- Rounded corners
These are achieved through the following CSS properties:
background-gradient: (color)
background-gradient-angle: (number)
corner-ne-radius: (length)
corner-nw-radius: (length)
corner-se-radius: (length)
corner-se-radius: (length)
corner-radius: (length){1,4} (shorthand for all corners)
What's Currently Supported by the Renderer?
- Most border, padding and margin and positioning CSSproperties (except for the
height property)
- Text alignment horizontally and vertically, text indents too
- Lists, ordered and unordered. Advanced numbering is not yet supported
- Tables, almost all of it. Cell combinations work quite well as far as I tested them
- Fonts (partially) and Colors
- Backgrounds (just color)
Points of Interest
What can I say, this is one of the most fun projects I've ever been involved with. And so far, it runs beautifully and checks its original design goals.
I am planning to give it full rendering support, to the day that you may visualize a web page just as a good web browser would; and why not, make a WYSIWYG HTML editor to give amazing HTML editing power to your applications.
I'm also planning to make sure it runs perfectly well on Mono and on Mobile platforms.
In the next few days, I'll publish a list of supported HTML tags and CSS properties.
History
- Jan 08 2009: First release
- Jan 29 2009: Minor bugs fixed
- Text Encoding of samples
- Large paragraphs and fonts because of Culture
- License
| You must Sign In to use this message board. |
|
|
 |
|
 |
What's the latest on making a Compact Framework version of this? I think it would be fine to accept less funcitonality - no rounded corners, no tool-tip control, etc. But a good basic HTML rendering engine for CF would be mighty valuable.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi there
You got my vote for 5 stars. I was wondering when it's likely for you to release the next version of you fantastic library? I only ask as I really really really don't want to use the web browser control to render a few tabular reports and documents. Also adding print functionality would be ace.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
As a C# (.Net) programmer who who coded mostly Java for yrs before coming back to the .Net fold, I missed the ability to put HTML (and it's formatting benefits) on almost any control in Java. Your article has given me tons of ideas in terms of getting all the functionality back in .Net through classes or factories .. Thanks !!!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This is an amazing accomplishment.
I looked at your code, but didnt' want to make a weekend out of that or anything.
I was unable to tell...did you use recursion at all in this?
Thanks, toddmo
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
On some systems using this library I have seen issues where the font metrics are broken, leading to things like "editions currently" being rendered as "editionscurrently" as the space between them is compressed down to nothing.
I have a fix of sorts:
In HtmlPanel.cs (and I guess the HtmlLabel.cs) in the OnPaint method change the line:
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; to
e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault; We saw the fails in some Vista and Win2000 systems, but saw it working fine in Win7 systems. Edit: This could be related to Sans-Serif font. We are using Tahoma
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This HTML Code:
<P align=center>abc<HR>def</P> Does not center the text. If the "HR" is removed, everything works well.
Any idea?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
In the article it says this code is under the BSD license (can use it in a commercial app), but the demo application says it is under the GPL (for all intents and purposes, can't use it in a commercial app).
Can you clearify which it is?
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Spaces after punctuation (comma, point, colon; etc.) shouldn't disappear. I don't now where is the best place to deal with this situation. Any suggestions? wasn't being delt with, so I added a Replace(" "," ") in the WordBox Text property.
Joel
modified on Tuesday, July 7, 2009 6:45 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thank you for a fantastic project and a very good work!
I need Font Tags to honour the size attribute. So I tried to fix it by adding code in HtmlTag.TranslateAttributes:
case HtmlConstants.size: switch (t) { case HtmlConstants.HR: box.Height = TranslateLength(value); break; case HtmlConstants.FONT: int idx; if (int.TryParse(value, out idx)) { string[] fsize = { "xx-small", "x-small", "small" , "medium", "large", "x-large", "xx-large" }; if (idx > 0 && idx < 8) { box.FontSize = fsize[idx-1]; } } else {
box.FontSize = value; } break; }
break;
It seems to be working ok, but when the font size is bigger then the rest of the font in the same line/paragraph, any link (<A> tag) is displayed with underline in the wrong y position. Links start looking like Strike Through text.
image example: http://i40.tinypic.com/mlle1i.gif[^]
Any ideas to fix this will be greatly apreciated.
Joel
modified on Tuesday, June 23, 2009 5:24 PM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
|
 |
|
 |
First: Thanks for this great part of work.
I had the same problem: "br" does not break lines. Fix is simple here:
Just change "CssDefaults.cs" line#26 to
dir, hr, menu, pre, br { display: block }
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
Thank you, thank you.
What about < and > ? they don't seem to work.
p>Examples: <b> <i> <br> <img> <a> </p>
Don
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hello,
I am using this control when rendering information about application state nicely and when showing shortcuts to some application actions (like in Explorer). Standard WebBrowser didn't work properly under Mono and Ubuntu, this very nice renderer works in my case under Mono without any problems.
First thing, which caused problems, is missing of Navigating event, which is present in WebBrowser control. There is possibility to call static methods, when clicked on link, but I need to process custom link strings (like: <a href="menu:file/open"> ). I solved it by hacking source code and adding this event directly to HtmlPanel.
Second small problem is not supporting single quoted parameters (<table width='100%'> doesn't work, <table "width=100%"> yes)
Thanks for great work Jan Prochazka
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi, I used this hack (all changes are in file HtmlPanel.cs): put this somewhere in HtmlPanel class:
#region Events public event LinkClickEventHandler LinkClick; #endregion
in the OnMouseClick method, replace
CssValue.GoLink(box.GetAttribute("href", String.Empty)); with
string href = box.GetAttribute("href", string.Empty); if (LinkClick != null) { LinkClickEventArgs ev = new LinkClickEventArgs(); ev.Href = href; LinkClick(this, ev); if (ev.Handled) return; } CssValue.GoLink(href);
Put somewhere in the some namespace (eg. after definition of class HtmlPanel) definitions of delegate and event class:
public class LinkClickEventArgs : EventArgs { public string Href; public bool Handled = false; }
public delegate void LinkClickEventHandler(object sender, LinkClickEventArgs e);
hope this helps...
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
You could possibly make a browser by using HTTP requests, caching it, then feeding it the code. I am not sure how it would handle images though.
EDIT: HTTP linking are opening in FF, and throwing an exception.
People think it must be fun to be a super genius, but they don't realize how hard it is to put up with all the idiots in the world. - Calvin (from Calvin and Hobbes)(The Indispensable Calvin and Hobbes, p105-3)
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Jose Manuel,
I have made some changes for support base64 encoded images in the renderer (you can find more info at http://dean.edwards.name/weblog/2005/06/base64-ie/[^])
I need to change only two sections of code:
HtmlTags.cs Constructor:
Change:
foreach (Match att in atts) { string[] chunks = att.Value.Split('=');
if (chunks.Length == 1) { if(!Attributes.ContainsKey(chunks[0])) Attributes.Add(chunks[0].ToLower(), string.Empty); } else if (chunks.Length == 2) { string attname = chunks[0].Trim(); string attvalue = chunks[1].Trim();
if (attvalue.StartsWith("\"") && attvalue.EndsWith("\"") && attvalue.Length > 2) { attvalue = attvalue.Substring(1, attvalue.Length - 2); }
if (!Attributes.ContainsKey(attname)) Attributes.Add(attname, attvalue); } }
With:
foreach (Match att in atts) { int splitIndex = att.Value.IndexOf('='); string name = (splitIndex > 0 ? att.Value.Substring(0, splitIndex) : att.Value).Trim().ToLower(); string value = splitIndex > 0 ? att.Value.Substring(splitIndex+1).Trim() : string.Empty;
if (value.StartsWith("\"") && value.EndsWith("\"") && value.Length > 2) value = value.Substring(1, value.Length - 2);
if (!Attributes.ContainsKey(name)) Attributes.Add(name, value); }
Css Value.cs
Add using System.Text.RegularExpressions;
Change the function public static Image GetImage(string path) with this code:
public static Image GetImage(string path) { Regex base64encoded = new Regex("^data:(.*);base64,(.*)", RegexOptions.IgnoreCase);
Match match = base64encoded.Match(path); if (match.Success) { using (MemoryStream buffer = new MemoryStream(Convert.FromBase64String(match.Groups[2].Value))) { return Image.FromStream(buffer); } }
object source = DetectSource(path);
FileInfo finfo = source as FileInfo; PropertyInfo prop = source as PropertyInfo; MethodInfo method = source as MethodInfo;
try { if (finfo != null) { if (!finfo.Exists) return null; return Image.FromFile(finfo.FullName); } if (prop != null) { if (!prop.PropertyType.IsSubclassOf(typeof (Image)) && !prop.PropertyType.Equals(typeof (Image))) return null; return prop.GetValue(null, null) as Image; } if (method != null) { if (!method.ReturnType.IsSubclassOf(typeof (Image))) return null; return method.Invoke(null, null) as Image; }
return null; } catch { return new Bitmap(50, 50); } }
Saludos
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I did the following changes to HtmlPanel (under VS2008):
1. Removed the following line in OnPain()
if (!(this is HtmlLabel)) e.Graphics.Clear(SystemColors.Window);
2. Replaced in the constructor
SetStyle(ControlStyles.Opaque, true); by SetStyle(ControlStyles.SupportsTransparentBackColor, true);
and now it supports transparent background! And it doesn't seems to interfere with the demo examples that use a specific stylesheet.
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
|
 |
|
 |
Hi, Great Great Project You Have Here.
Works so well, but i notice you have not added support for a simple html line break? ( < br > / < br /> )
Could someone advise me on adding support for this tag?
Thanks, Andy
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Seems like a nice control. Great job!
I'm trying to use the control to render an html table and I want the table to cover the full height and width of the control (have a background color for the table). A width specification of 100% seems to be honored properly, but not a height specification of 100% -- that is, a blank area is displayed beneath the html table. Any suggestions?
Example html that I'm using:
<html> <head> <style type="text/css"> body { border: 1px solid #6ba8de; margin: 0px; padding: 4px; font: 9pt 'Segoe UI'; background-color: #E7EBF7; } .header { font: 12pt; } .key { color: #404080; font-weight: bold; border-style: none; } .value { color: #404040; border-style: none; } .summary { border-style: none; width: 100%; height: 100%; } </style> </head> <body> <table class="summary"> <tr> <td colspan="2" class="header">Container</td> </tr> <tr> <td class="key">Type</td> <td class="value">$Type</td> </tr> ... </table> </body> </html>
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|