Click here to Skip to main content
15,881,248 members
Articles / Programming Languages / C#

Problem with Response.Write - Changing Dynamic Content Without Corruption

Rate me:
Please Sign up or sign in to vote.
4.45/5 (9 votes)
15 May 2008CPOL2 min read 53.4K   164   22   8
Changing HTML content of an ASP.NET page using a new technique that handles Response.Write().

Introduction

When you need to dynamically control the HTML that is generated from your ASP.NET page or control, a popular technique to do that is to override the render function: protected override void Render(HtmlTextWriter writer). It is a function where you can get great power over your control. The function simply sends an HtmlTextWriter, which is a stream where you can write anything and it will appear on the client screen, as we can see in the code below.

C#
protected override void Render(HtmlTextWriter writer)
{
    writer.Write("SayAnthing");
}

The Problem

Now, let us change the page data instead of replacing it. The attached sample contains two ASPX pages, both with the same HTML structure as follows:

HTML
<table> <tr> <td>
        Hello ( <%Response.Write(@"Mr. XYZ"); %> $MSG$       
</td> </tr> </table>

All we need to do is replace $MSG$ with a message "How R U?" by capturing the rendering event. The required result should be as in the following figure:

GoodResult.PNG

Let us write the code:

C#
protected override void Render(HtmlTextWriter writer) 
{
    // Create your HTMLTestWriter
   System.Text.StringBuilder oPageContents = new System.Text.StringBuilder();
    System.IO.StringWriter oSW = new System.IO.StringWriter(oPageContents);
    System.Web.UI.HtmlTextWriter oHtmlWriter = new System.Web.UI.HtmlTextWriter(oSW);
    
    // Give the page a fake streamer to capture HTML contents.
    base.Render(oHtmlWriter); 
    oPageContents = oPageContents.Replace("$MSG$", "How R U ?");
    
    // Write data back into the original stream
    System.IO.StringReader oSR = new System.IO.StringReader(oPageContents.ToString ());
    writer.Write(oSR.ReadToEnd ()); 
}

Nothing strange about this code, and it can be found all over the internet. The idea behind it is that you replace the given HtmlTextWriter that you can only write into, with another one that streams data into a StringBuilder object, oPageContents. Once the HTML is in the oPageContents StringBuilder, you can read and modify it easily. The next step is to write your contents in the actual HtmlTextWriter that is passed as a parameter in the Render function. It is pretty straightforward :) Wait, the result is not what you want??!!

The strange thing is that you don't get the desired result; instead, you get the following result:

BadResult.PNG

I spent a couple of days searching why this happened. And finally, I found the answer. Response.Write() does not send its contents through base.Render that is used to render the control. Response.Write(), in fact, the HtmlTextWriter stream sends the data eventually to the Response object.

Diagram1.png

So what happens here is that while base.Render() is working, when Response.Write() takes place, it sends data directly out, while the other data are all stored in our local StringBuilder, and then we inject them in to the writer, but then it is too late, as the order of streaming output now has been corrupted.

Solution - HTML Streamer

HTMLStreamer solves this issue by acting as a double streamer. It streams data to a StringBuilder object as in our previous sample. At the same time, it streams data to a writer parameter of type HtmlTextWriter. The data is submitted synchronized to the Response object, while another copy is saved in the StringBuilder object.

C#
public class HTMLStreamer: System.IO.StringWriter 
{ 
    protected System.Web.UI.HtmlTextWriter mHtmlTextWriter;
    public HTMLStreamer(System.Text.StringBuilder oStringBuilder, 
                        System.Web.UI.HtmlTextWriter innerWriter) 
        : base(oStringBuilder)
     { 
        mHtmlTextWriter = innerWriter;
     }
    
    public override void Write(string value) 
    { 
        // Change content dynamically here.
        if (value.IndexOf("$MSG$")!=-1) 
        {
            value = value.Replace("$MSG$","How R U ?"); 
        }
        mHtmlTextWriter.Write(value); base.Write(value); 
    }
    
    public override void Write(char[] buffer, int index, int count)
    { 
        mHtmlTextWriter.Write(buffer, index, count);
        base.Write(buffer, index, count);
    } 

    public override void Write(char value) 
    { 
        mHtmlTextWriter.Write(value);
        base.Write(value);
    } 
}

Now back to the Render() function; it should be handled as follows:

C#
protected override void Render(HtmlTextWriter writer) 
{ 
    // Create your string builder.
    System.Text.StringBuilder oPageContents = new System.Text.StringBuilder();
   
    // Create HTMLStreamer and put both writer & oPageContents into it.
    HTMLStreamer oSW = new HTMLStreamer(oPageContents, writer); 
   
    // Create HTMLTextWriter that contains the double-stream class "HTMLStreamer"
    System.Web.UI.HtmlTextWriter oHtmlWriter = new System.Web.UI.HtmlTextWriter(oSW);
   
    // Get HTML contents.
    base.Render(oHtmlWriter); 
}

Enhancements

A delegate is called in all Write() functions of HTMLStreamer so that it can be used by the page to change the contents without the need to write into the HTMLStreamer itself and so it can be used as a separate DLL. If you have any idea on how to capture Response.Write(), please let me know.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Architect
Egypt Egypt
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Suggestion" If you have any idea on how to capture Response.Write() ... " Pin
Micke2nd14-Mar-12 4:42
Micke2nd14-Mar-12 4:42 
GeneralExcellent work! Pin
feguardia27-Oct-09 9:59
feguardia27-Oct-09 9:59 
AnswerSimple fix: Use &lt;%= %&gt; instead of &lt;%Response.Write%&gt; Pin
Rick L9-Oct-08 5:19
Rick L9-Oct-08 5:19 
GeneralGood work !!! Pin
Ashutosh Phoujdar21-May-08 22:16
Ashutosh Phoujdar21-May-08 22:16 
Learn t something new !!! Smile | :)

dnpro

GeneralAlternative Pin
Mat Fergusson20-May-08 0:16
Mat Fergusson20-May-08 0:16 
GeneralRe: Alternative Pin
Mohammad Said Hefny10-Aug-08 2:26
Mohammad Said Hefny10-Aug-08 2:26 
GeneralRe: Alternative Pin
J. Moreno22-Jun-09 8:02
J. Moreno22-Jun-09 8:02 
AnswerRe: Alternative Pin
Micke2nd14-Mar-12 4:31
Micke2nd14-Mar-12 4:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.