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!
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:
|
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
ListItem
s
specified with Value
s 1, 2, and 3:
|
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!
Further Reading:
Attachments