Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using HtmlTextWriter to Render Server Controls?

I'm writing the RenderContents() method of my ASP.NET server control. The method uses an HtmlTextWriter object to render the output content. For the control I'm writing, using the HtmlTextWriter's methods seems like it will require a lot of lines of code to open and close every tag and add every attribute to the stream. In the end I feel like I'm going to end up with code that is a lot longer than it needs to be.

I was thinking that if I used a chainable class such as StringBuilder, my code would be a lot cleaner to read and easier to write.

What I was wondering was, is there any reason to use the HtmlTextWriter object to render my entire control's contents? Other than the safety checks (I'm assuming) it includes to make sure you don't write tags in the wrong order or create invalid markup, I don't see a reason.

It seems like it would be easier to just do something like this:

protected override void RenderContents(HtmlTextWriter output)
{
    StringBuilder s = new StringBuilder();
    s.Append("lots")
     .Append("of")
     .Append("strings");

    output.BeginRender();
    output.Write(s.ToString());
    output.EndRender();
}

Is there any reason why this would be a bad idea?

Update
In response to Mehrdad Afshari's answer:
I didn't think much about the memory requirements of having a separate StringBuilder object instantiated. What about making a wrapper for HtmlTextWriter so that it can be chained so that an extra string isn't made.

public class ChainedHtmlTextWriter
{
    private HtmlTextWriter _W;
    public ChainedHtmlTextWriter(HtmlTextWriter writer)
    {
        _W = writer;
    }

    public ChainedHtmlTextWriter Write<T>(T value) 
    { 
        _W.Write(value); 
        return this; 
    }

    public ChainedHtmlTextWriter WriteLine<T>(T value)
    {
        _W.WriteLine(value);
        return this;
    }
}
like image 769
Dan Herbert Avatar asked Jan 28 '09 22:01

Dan Herbert


3 Answers

I work on an application where the developers followed the horrible path you're exploring. This harkens back to the days when you had to write your own ISAPI dlls that spit out html code. It is a constant headache to work in. If your code is mostly strings, then something is wrong.

Most of the code of this type that I change I instantiate server objects, configure their properties as desired, and then tell them to .RenderControl(writer). This makes the code much easier to read and work with. If there is a performance hit from the overhead this brings, I am willing to accept it (in fact, the application generally runs faster after I've made my changes, so anecdotally this isn't the case, but I haven't profiled my code).

One simple drawback to hard-coding your stuff in strings is when HTML standards change. The code I work on was written in 04/05, and since then <BR> has become <br /> and uppercase html tags aren't kosher anymore, etc. If they had been using server controls, those server controls have changed their outputted html without us needing to do anything. This is just one simple example.

EDIT: Oh, and btw, BeginRender and EndRender don't have any implementation. They are placeholders for you to override and provide custom functionality in a HtmlTextWriter-derived class.

EDIT2: Sometimes it's a bit onerous to always use server controls, like for containers and stuff. I'd be doing lots of .Controls.Add() and then render the container later. So sometimes I do this:

writer.AddAttribute(HtmlTextWriterAttribute.Class, "myContainerClass");
writer.RenderBeginTag(HtmlTextWriterTag.Div);
// do some stuff, .RenderControl on some other controls, etc.
writer.RenderEndTag();

As mentioned, this will render correct html even if the html of a div changes in the future, cause I don't have any hard-coded strings.

like image 71
sliderhouserules Avatar answered Nov 03 '22 18:11

sliderhouserules


Performance-wise, this will require more string copies to be done. HtmlTextWriter writes directly to the output buffer. StringBuilder on the other hand, has its own buffer. When you call ToString on the StringBuilder, a new string has to be built and then it will be written to the output buffer by output.Write. It requires much more work to be done.

like image 35
mmx Avatar answered Nov 03 '22 18:11

mmx


I don't think you ought to be calling BeginRender/EndRender, thats done by the page.

I can't see how using string builder would save any work over using the HtmlTextWriters own methods.

like image 30
AnthonyWJones Avatar answered Nov 03 '22 18:11

AnthonyWJones