tags:

views:

87

answers:

2

I have two pages on my site which are populated with content from a database as well as having user-entered fields (don't ask!). The pages also contain a ListView with a nested DataList. There are buttons on these pages which when clicked grab the html content of the page, write it to a HtmlTextWriter then get the text and put it into an email.

What I need to do is replace any TextBox / DropDownLists in the html source with string literal equivalents before putting into the email.

My aspx code so far looks something like this:

<div id="mailableContent" runat="server">
    <asp:TextBox ID="txtMessage" runat="server"/>
    <asp:Label ID="lblContentFromDb" runat="server"/>
    <asp:ListView ID="lvwOffices" runat="server">
      //loads of stuff here including more textboxes for the user to fill in
    </asp:ListView>
</div>

and the codebehind is something like this:

StringBuilder stringBuilder = new StringBuilder();
StringWriter writer = new StringWriter(stringBuilder);
HtmlTextWriter htmlWriter = new HtmlTextWriter(writer);
mailableContent.RenderControl(htmlWriter);
MailMessage message = new MailMessage();
//do more stuff to set up the message object
message.Body = stringBuilder.ToString();
//send the message

My ideas so far are to 1. manually set any textboxes to Visible=false then populate literal controls with corresponding textbox values which is rather messy and tedious. Note I have to strip out input controls otherwise html for the email needs to be wrapped with a form elemen which I don't really want to do.

Is there a better way to do all this, I'm thinking that perhaps doing some .Net1.1 style xslt transforms with page content defined in xml files might be a better way to approach this, but am unsure if this will handle my requirement where I'm currently using a ListView with a nested DataList.

+1  A: 

I find what you are describing to have a lot of overhead. Does your email template stay prety much the same? if so why not simply have a simple html template with html 3 code. Then simply read this file from disk and replace specific peices (ie. ##Name##) with the dynamic content. This way you have complete control over the html being sent via email and you can control what users input.

this would also limit the amout of work to make the html compatible with email clients.

Clarification: In the preceding suggestion, I propose that the UI implementation and the Email implementation be distinct, this in turn allows to to compose the email with more flexibility. Without using

mailableContent.RenderControl(htmlWriter);

this also allows you to compose the contents of the ListView to your specifications.

Alexandre Brisebois
Thanks for the suggestion, but how does this address the ListView/DataList requirement?
mdresser
I'll take a second go at it, I'm not sure I understood the question.
Alexandre Brisebois
+1  A: 

Couple of ideas:

  1. Use RegEx to replace the html output of the textboxes and dropdownlists with plaintext. (Tricky, with the complication of finding the <option selected="selected"> stuff.

  2. Do what I do:

    • Create an interface e.g. IPlainTextable
    • Make the interface enforce a boolean property called PlainTextMode (set false by default)
    • Extend TextBox and DropDownList with your own controls that implement IPlainTextable
    • In the Render section of your extended webcontrols, render out the plaintext value if PlainTextMode is true. e.g for your subclass of TextBox

      protected override void Render(HtmlTextWriter writer)
      {
         if (PlainTextMode)
            writer.WriteLine(this.Text);
         else
            base.Render(writer);
      }
      
    • Before rendering out your page, run through all the IPlainTextable controls and set the PlainTextMode to true.

I have written a nifty little method for iterating through a nested control set:

public static List<T> FindControlsOfType<T>(Control ctlRoot)
{
    List<T> controlsFound = new List<T>();

    if (typeof(T).IsInstanceOfType(ctlRoot))
        controlsFound.Add((T)(object)ctlRoot);

    foreach (Control ctlTemp in ctlRoot.Controls)
    {
        controlsFound.AddRange(FindControlsOfType<T>(ctlTemp));
    }

    return controlsFound;
}

So you would just do something like:

foreach (IPlainTextable ctl in FindControlsOfType<IPlainTextable>(this))
{
    ctl.PlainTextMode = true;
}

and then do your render to string after that...

James McCormack