Rendering C# Objects to Html

13,802

Solution 1

I can't agree more with John Feminella. Integrating Html rendering directly into your domain entities is a horrid pollution of your domain with a completely arbitrary and external concern. Like John said, you will make your entities very brittle by doing that, and break both of those critical rules: Separation of Concerns and Single Responsibility.

From the choices you gave, #3 is the closest to an appropriate way to do it. You need not write your own template engine. There are plenty free and ready-made template engines on the net that will do the job more than adequately (NVelocity, StringTemplate, Brail, etc. etc.)

Keep rendering where it belongs...in your presentation/view, and don't pollute your domain with higher level concerns.

Solution 2

I once had a need to render a collection of any type to an Html Table. I created an extension method on IEnumerable<T> that had overloads for css and the like. You can see my blog post about it here:

http://crazorsharp.blogspot.com/2009/03/cool-ienumberable-extension-method_25.html

It uses reflection to get all the properties of the object, and renders a nice little table. See if that would work for you.

[System.Runtime.CompilerServices.Extension()]
public string ToHtmlTable<T>(IEnumerable<T> list, string propertiesToIncludeAsColumns = "")
{
    return ToHtmlTable(list, string.Empty, string.Empty, string.Empty, string.Empty, propertiesToIncludeAsColumns);
}

[System.Runtime.CompilerServices.Extension()]
public string ToHtmlTable<T>(IEnumerable<T> list, string tableSyle, string headerStyle, string rowStyle, string alternateRowStyle, string propertiesToIncludeAsColumns = "")
{
    dynamic result = new StringBuilder();
    if (String.IsNullOrEmpty(tableSyle)) {
        result.Append("<table id=\"" + typeof(T).Name + "Table\">");
    } else {
        result.Append((Convert.ToString("<table id=\"" + typeof(T).Name + "Table\" class=\"") + tableSyle) + "\">");
    }

    dynamic propertyArray = typeof(T).GetProperties();

    foreach (object prop in propertyArray) {
       if (string.IsNullOrEmpty(propertiesToIncludeAsColumns) || propertiesToIncludeAsColumns.Contains(prop.Name + ",")) {
        if (String.IsNullOrEmpty(headerStyle)) {
            result.AppendFormat("<th>{0}</th>", prop.Name);
        } else {
            result.AppendFormat("<th class=\"{0}\">{1}</th>", headerStyle, prop.Name);
        }
      }
    }

    for (int i = 0; i <= list.Count() - 1; i++) {
        if (!String.IsNullOrEmpty(rowStyle) && !String.IsNullOrEmpty(alternateRowStyle)) {
            result.AppendFormat("<tr class=\"{0}\">", i % 2 == 0 ? rowStyle : alternateRowStyle);

        } else {
            result.AppendFormat("<tr>");
        }
        foreach (object prop in propertyArray) {
            if (string.IsNullOrEmpty(propertiesToIncludeAsColumns) || propertiesToIncludeAsColumns.Contains(prop.Name + ",")) {
                object value = prop.GetValue(list.ElementAt(i), null);
                result.AppendFormat("<td>{0}</td>", value ?? String.Empty);
            }
        }
        result.AppendLine("</tr>");
    }

    result.Append("</table>");

    return result.ToString();
}

Solution 3

This is the job of your view, not your controllers or models. As you suspect, using method (1) will make your changes very brittle. Instead, write a view fragment for each way in which you'd like to render a particular domain entity. Then simply pull in the appropriate views to build your final page.

Solution 4

I sincerely prefer method A. The HTML is already a standard so you shouldn't beat yourself up too much with intermediary formats such as XML (via XSL - method 2) or a custom format (method 3).

If writing in/for ASP.NET MVC you could however build an .ASCX (user control) that would accept an object of your type and render the appropiate HTML. And you don't get the nasty C# indentation and no highlighting nor autocomplete.

You could also write multiple User views to accomodate different scenarios for one object type.

Solution 5

Is there a reason you do not want to create a custom control .ascx file that takes domain entities object as an argument and then you can make the gui any way you want. This way you could dynamically add the controls to the page and just set a property to populate them.

Edit 1: Just render the control to an HTMLTextWriter instead of placing it on the page.

        StringBuilder outString = new StringBuilder();
        StringWriter outWriter = new StringWriter(outString);
        HtmlTextWriter htmlText = new HtmlTextWriter(outWriter);
        System.Web.UI.WebControls.Label lbl1 = new System.Web.UI.WebControls.Label();
        lbl1.Text = "This is just a test!";
        lbl1.ToolTip = "Really";
        lbl1.RenderControl(htmlText);
        ViewData["Output"] = outString.ToString();

Output

<span title="Really">This is just a test!</span>

Of course use your custom control instead of a built in control but this was a quick and easy demo.

Share:
13,802
rabashani
Author by

rabashani

Updated on July 20, 2022

Comments

  • rabashani
    rabashani almost 2 years

    We have bunch of Domain Entities which should be rendered to an html format, which shows their detail in a pop up window.

    I would be glad to do something like this:

    Product product = new Product(...);
    product.ToHtml();  // or: HtmlRenderer.Render(Product);
    

    but my main problem is how to do this stuff from behind. I have 3 different answers:

    1. Render By Code:

    I can simply write my code for rendering the Html inside the ToHtml Method (C#) - the problem it is that it is too static. if you would like to move a little bit the header to the middle you should change code. moreover, it is very hard to read Html indentation in C#.

    2. Using XSL:

    XSL Files can easily manage the Html template and using XSLT I can transform XML file to the right place of the documents. the parser already written by someone else (just need to learn the syntax) ** for this we will need that each object could serialize to Xml. and if the object changed -> the Xml will be changed --> the xslt need to be changed too ** this will also give me the option to indent the html easily for example: adding css capabilities and\or change the html design

    3. using other template engine:

    Write my own C# -> Html template Engine so it will read the template from file (*.template) and will insert the right property in the right place of the template using reflection. ** in this solution we have many issues that we can think of, for example: how the syntax should be like? is this thing ok? %Name% %Description% and how we can handle arrays? ** maybe we can use an existing engine (Brail or T4-Templating)?

    What do you prefer? do you know a good engine? for now I prefer the second solution, but it gonna be very slow.

    thanks

  • rabashani
    rabashani almost 15 years
    I am not working in a web team, I am rendering objects-as-html and send them to anyone else - who can use this in wpf\palm\web-site or other technology, so I guess ascx is not good for me.
  • rabashani
    rabashani almost 15 years
    I am not working in a web team, I am rendering objects-as-html and send them to anyone else - who can use this in wpf\palm\web-site or other technology, so I guess ascx is not good for me
  • shahkalpesh
    shahkalpesh almost 15 years
    In that case, you could serialize it in JSON format & send it over for other users to use it.
  • Andrei Rînea
    Andrei Rînea almost 15 years
    Then use method 2 - an XML that would be translated to the proper format where you need to.
  • Adam A
    Adam A almost 15 years
    Sorry, to clarify, you should make your objects into Controls in ASP.Net and then you can use the above method to serialize them into HTML. This probably is too big a change though if your classes don't already inherit from System.Web.UI.Control
  • Y-H
    Y-H almost 15 years
    But you can still render your control to an html text writer and pass that value. That way you can still design and maintain an ascx for convenience and simplicity over that of writing your own xsl or template engine.
  • Y-H
    Y-H almost 15 years
    added code to my answer to show how ascx could work just fine.