When you think ASP, think...
Recent Articles
All Articles
ASP.NET Articles
ASPFAQs.com
Message Board
Related Web Technologies
User Tips!
Coding Tips
Search

Sections:
Book Reviews
Sample Chapters
Commonly Asked Message Board Questions
JavaScript Tutorials
MSDN Communities Hub
Official Docs
Security
Stump the SQL Guru!
Web Hosts
XML
Information:
Advertise
Feedback
Author an Article
Jobs

ASP ASP.NET ASP FAQs Message Board Feedback ASP Jobs
 
Print this Page!
Published: Wednesday, December 13, 2006

Emailing the Rendered Output of an ASP.NET Web Control in ASP.NET 2.0

By Scott Mitchell


Introduction


In a previous 4Guys article, Emailing the Rendered Output of an ASP.NET Web Control, I showed how to programmatically render the HTML of an ASP.NET Web control (such as a GridView) and then send that HTML through email. This technique is useful in scenarios where you want to let the user email themselves the web page, or portions of the page. For example, in a typical eCommerce website, after completing an order the user is taken to an order summary page that would likely include a Web control that lists the items purchased, the applicable tax, and the total due. You could include a button on this page that, when clicked, emails the user this information.

While the code presented in Emailing the Rendered Output of an ASP.NET Web Control and its follow-up article, Enhancing the 'Email the Rendered Output of an ASP.NET Web Control' Code, works great in ASP.NET version 1.x, the same code results in an exception in ASP.NET 2.0. In this article we will look at why this exception occurs and how to circumvent it, exploring examples in both VB and C#. Read on to learn more!

- continued -

A Quick Review of Rendering the Contents of a Web Control


The previous Emailing the Rendered Output of an ASP.NET Web Control articles explore the ins and outs of programmatically rendering a Web control's HTML, but let's take a moment to quickly review the basics. All Web controls have a RenderControl(HtmlTextWriter) method that, when invoked, renders the control, emitting its resulting markup to the passed-in HtmlTextWriter instance.

This programmatic rendering is performed using the following code:

// C#
StringBuilder SB = new StringBuilder();
StringWriter SW = new StringWriter(SB);
HtmlTextWriter htmlTW = new HtmlTextWriter(SW);
WebControlToRenderID.RenderControl(htmlTW);

'VB
Dim SB As New StringBuilder()
Dim SW As New StringWriter(SB)
Dim htmlTW As New HtmlTextWriter(SW)
WebControlToRenderID.RenderControl(htmlTW)

After the above code is executed, the control's rendered HTML can be accessed from the StringBuilder object's ToString() method (i.e., SB.ToString()).

In ASP.NET 1.x, this code works wonderfully unless the Web control being rendered is a control that must reside within a WebForm or contains a child control that must reside in a WebForm. For example, Button Web controls must reside within a WebForm. Consequently, if you attempt to use the above code to render a Button Web control or a DataGrid or GridView that contains a Button (or LinkButton or ImageButton) Web control, an exception will be raised complaining that the control is not in a WebForm. This check is performed by the Page class's VerifyRenderingInServerForm() method.

This problem, and three workarounds, are discussed in detail in the Enhancing the 'Email the Rendered Output of an ASP.NET Web Control' Code article. The simplest workaround is to create a custom base Page class that overrides the Page class's VerifyRenderingInServerForm() method so that it ignores checking whether or not the control is rendering in a WebForm.

Problems in ASP.NET 2.0


If you use the code presented in the Emailing the Rendered Output of an ASP.NET Web Control articles to attempt to render the email output of a control in ASP.NET 2.0, you may wind up with the following exception message (depending on what, exactly, you are attempting to render programmatically):
RegisterForEventValidation can only be called during Render();
This is due to ASP.NET 2.0's new event validation feature. In a nutshell, event validation is a technique used by ASP.NET 2.0 to ensure that the data being sent back to the server on postback is an expected value and is designed to help prevent injection attacks. As K. Scott Allen notes in his blog entry ASP.NET Event Validation and "Invalid Callback Or Postback Argument": one example of event validation in action is with a DropDownList Web control. Imagine that the DropDownList has three ListItems specified with Values 1, 2, and 3:

<asp:DropDownList runat="server" id="MyDropDownList">
  <asp:ListItem Value="1">Item 1</asp:ListItem>
  <asp:ListItem Value="2">Item 2</asp:ListItem>
  <asp:ListItem Value="3">Item 3</asp:ListItem>
</asp:DropDownList>

If, on postback, the page sends back the value 4 for the DropDownList MyDropDownList, the ASP.NET runtime will throw an exception because it expects a value of 1, 2, or 3. This is the ideal behavior if you really only wanted to allow the values 1, 2, or 3, because then a value of 4 indicates that something fishy is going on. However, the DropDownList's items might have been altered on the client-side intentionally, adding a list item with a value of 4. In that case, the exception raised by event validation is a nuisance.

During the page's lifecycle, those controls that participate in event validation "register" themselves through the ClientScriptManager class's RegisterForEventValidation method. The RegisterForEventValidation method throws the "RegisterForEventValidation can only be called during Render()" if it is invoked before the Render stage. The code to programmatically render and email a Web control's markup usually is placed in the Page_Load event handler or in the event handler for a Button's Click event. But these event handlers fire before the Render stage. Therefore, when the control being rendered registers itself for event validation, the RegisterForEventValidation method raises this exception because the Render stage has not yet been reached.

Of course, this exception only occurs when programmatically rendering a control that participates in event validation (or when the control being rendered has a child control that participates in event validation), and not all controls participate in event validation. Only those that are decorated with the SupportsEventValidation attribute go through the event validation workflow. The following table from K. Scott Allen's blog entry lists those controls that participate in event validation:

HtmlAnchor HtmlButton HtmlInputButton
HtmlInputCheckBox HtmlInputHidden HtmlInputImage
HtmlInputText HtmlInputPassword HtmlInputRadioButton
HtmlInputReset HtmlInputSubmit HtmlSelect
HtmlTextArea BulletedList Button
Calendar CheckBox Table
ChildTable WizardChildTable DataControlButton
ImageButton DataControlImageButton LinkButton
DataControlLinkButton DataControlPagerLinkButton DataGridLinkButton
DetailsView DropDownList FormView
GridView HiddenField ImageMap
LayoutTable ListBox Menu
PagerTable RadioButton RadioButtonList
TextBox TreeView WizardDefaultInnerTable
CatalogZone ConnectionsZone EditorZone
WebPartZone ZoneButton ZoneLinkButton

Fixing the Event Validation Exception for Programmatically Rendered Controls


There are a couple of approches that can be taken to allow for a control to be programmatically rendered without raising the event validation-related exception. The simplest approach is to simply disable event validation for the page. This can be done by setting the EnableEventValidation attribute in the <% @Page %> directive to False:

<% @Page EnableEventValidation="False" ... %>

If you are using the base Page class technique to override the Page class's VerifyRenderingInServerForm() method, you can always override the EnableEventValidation property there, instead. The download at the end of this article provides VB and C# examples of how to override this property to have it return False through the base Page class.

The other option is to leave the event validation feature enabled, but just wait to programmatically render the control until the Render stage. By doing this, when the control registers for event validation it will be in the Render stage and so the "RegisterForEventValidation can only be called during Render()" exception won't be raised. To accomplish this, you'll need to set some flag in the Button's Click event handler (or wherever you decide to programmatically render the control). Then, override the Page's Render(HtmlTextWriter) method and run the code to generate and email the selected control's HTML output. See the code download for examples in VB and C#.

Sending the Rendered Control's Markup in an Email Message


Once the control's markup has been rendered and stored in a string, we're ready to email it to a specified recipient. Simply send an HTML-formatted email using ASP.NET 2.0's MailMessage and SmtpClient classes in the System.Net.Mail namespace. See Sending Email in ASP.NET 2.0 and its follow-up article, HTML-Formatted Emails, Attachments, and Gracefully Handling SMTP Exceptions, for more information.

Happy Programming!

  • By Scott Mitchell


    Further Reading:


  • Emailing the Rendered Output of an ASP.NET Web Control
  • Enhancing the 'Email the Rendered Output of an ASP.NET Web Control' Code
  • Sending Email in ASP.NET 2.0
  • Attachments


  • Download the Code Discussed in this Article (VB and C#)


  • ASP.NET [1.x] [2.0] | ASPMessageboard.com | ASPFAQs.com | Advertise | Feedback | Author an Article