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

ASP ASP.NET ASP FAQs Message Board Feedback
 
Print this Page!
Published: Wednesday, October 22, 2003

Enhancing the 'Email the Rendered Output of an ASP.NET Web Control' Code, Part 2

By Scott Mitchell


  • Read Part 1

  • In Part 1 we saw one technique for rendering the contents of a DataGrid that contains in its control hierarchy controls that require they be in a server-side form. In this final part of this article we'll look at another related approach to solve the same problem.

    - continued -

    Creating a New Page Instance for Rendering


    Rather than rendering the server-side form defined in the ASP.NET Web page's HTML portion, we can programmatically create a new Page instance with a new server-side form instance. We can then add the DataGrid (or whatever Web control we want to email the rendered contents of) to the server-side form, and then render the Page instance by calling the RenderControl() method. (This technique was thought up by Andy Smith, whose knowledge of ASP.NET control development is best shown by his numerous free ASP.NET server controls available at MetaBuilders.com.)

    The code for this approach is shown below. Note that we don't need to programmatically set the Visible property of any controls. Rather, we just create a new Page instance and a new server-side form, build up the control hierarchy, add the DataGrid, and call the programmatically-created Page's RenderControl() method. We don't need any Panel controls either.

    <% @Import Namespace="System.Web.Mail" %>
    <% @Import Namespace="System.IO" %>
    <% @Import Namespace="System.Data" %>
    <% @Import Namespace="System.Data.SqlClient" %>
    <script language="vb" runat="server">
      ... Page_Load & BindData() methods omitted for brevity ...
      ... Refer to Emailing the Rendered Output of an ASP.NET Web Control for their code ...
    
      Sub EmailDataGrid(sender as Object, e as EventArgs)
        'Save the position of the DataGrid in the myForm Controls collection
        Dim dgControlPosition as Integer = myForm.Controls.IndexOf(dgPopularFAQs)
        
        'Create a new Page instance and a server-side form
        Dim p as New Page()
        Dim hf as New HtmlForm()
        
        'Build up the control hierarchy
        p.Controls.Add(hf)
        hf.Controls.Add(dgPopularFAQs)
        
        Dim SB as New StringBuilder()    
        Dim SW as New StringWriter(SB)
        Dim htmlTW as New HtmlTextWriter(SW)
            
        'Call the page's RenderControl() method
        p.DesignerInitialize()
        p.RenderControl(htmlTW)
        
        p = Nothing
        
        'Add the DataGrid back to myForm
        myForm.Controls.AddAt(dgControlPosition, dgPopularFAQs)
           
        Dim dataGridHTML as String = SB.ToString()
        
        'Now, send the email
    	'Create an instance of the MailMessage class
    	Dim objMM as New MailMessage()
    
    	'Set the properties
        objMM.To = txtEmailAddy.Text
        objMM.From = "mitchell@4guysfromrolla.com"
    
        'Send the email in text format
        objMM.BodyFormat = MailFormat.Html
    
        'Set the subject
        objMM.Subject = "DataGrid Emailing"
    
        'Set the body - use VbCrLf to insert a carriage return
        objMM.Body = "<font face=""Verdana"">This is an example of emailing a " & _
                 "DataGrid Web control via an ASP.NET Web page." & dataGridHTML
    	
        SmtpMail.Send(objMM)
      End Sub
    </script>
    
    <form runat="server" id="myForm">
      <asp:datagrid id="dgPopularFAQs" runat="server"
            AutoGenerateColumns="False"
            Font-Name="Verdana"  Width="85%"
            HorizontalAlign="Center"
            ItemStyle-Font-Size="9pt"
            AlternatingItemStyle-BackColor="#dddddd">
    	     
         <HeaderStyle BackColor="Navy" ForeColor="White" Font-Bold="True"
              Font-Size="13pt" HorizontalAlign="Center" /> 
    	       
         <Columns>
           <asp:EditCommandColumn EditText="Edit" ... />
           <asp:BoundColumn HeaderText="FAQ ID" DataField="FAQID"
                   ItemStyle-HorizontalAlign="Center" />
           <asp:BoundColumn HeaderText="Question" DataField="Description" />
           <asp:BoundColumn HeaderText="Total Views" DataField="ViewCount"
                  DataFormatString="{0:#,###}" ItemStyle-HorizontalAlign="right" />
         </Columns>
      </asp:DataGrid>
    
      <p>
      <asp:TextBox id="txtEmailAddy" runat="server" />
      <asp:Button Text="Email DataGrid" OnClick="EmailDataGrid" runat="server" />
    </form>
    

    A couple things to notice here - before adding the dgPopularFAQs DataGrid to the programmatically-created server-side form, we needed to first remember where in the Controls collection of myForm it belonged. This is because after we render the contents of the programmatically-created Page, we'll need to place the DataGrid back in myForms Controls collection, and we want to place it back in the right place.

    Second, note that we call the Page class's DesignerInitialize() method prior to calling the RenderControl() method. This causes the Page class's protected InitRecursive() method to be called, which preps a number of properties needed for rendering. (Omitting this call to DesignerInitialize() will result in an exception being thrown since it won't properly mark that the server-side form has been rendered when the DataGrid's LinkButton is rendered...)

    With this approach, we don't need to worry ourselves with hiding the myForm server-side form since it is only rendered once the entire page life-cycle.

    Creating a Base Page Class That Overrides the VerifyRenderingInServerForm() Method


    The crux of the problem the two workarounds examined earlier in this article attempt to overcome is that the Page class's VerifyRenderingInServerForm() method throws an exception if the control being rendered is not located within a Web Form. Rather than trying to "fake out" the existing Page class's VerifyRenderingInServerForm() method (which is what the two previous workarounds do), we could just create our own base Page class and override the VerifyRenderingInServerForm() method.

    A base Page class is a class that extends the built-in System.Web.UI.Page, but adds to or customizes its functionality. Once this base Page class has been created, we can have our ASP.NET pages derive from it instead of System.Web.UI.Page. For more on this technique, be sure to read Using a Custom Base Class for your ASP.NET Page's Code-Behind Classes.

    Start by creating a class in your project named EmailReadyPage and use the following code:

    Public Class EmailReadyPage
        

    Inherits System.Web.UI.Page



        Public Overrides Sub VerifyRenderingInServerForm(ByVal control As System.Web.UI.Control)
            'DO NOTHING
        End Sub

    End Class

    This class derives from System.Web.UI.Page and overrides its VerifyRenderingInServerForm() so that it does nothing. That is, with the code above, when VerifyRenderingInServerForm() no exception is thrown, regardless of whether the control appears within a Web Form.

    Finally, configure your ASP.NET page(s) that send emails of rendered control markup to use the EmailReadyPage class as its base class by replacing the Inherits System.Web.UI.Page in the ASP.NET code-behind class with Inherits EmailReadyPage.

    Conclusion


    In this article we saw how to improve the code from Emailing the Rendered Output of an ASP.NET Web Control to allow for DataGrid's with LinkButtons or Buttons in their control hierarchy to be emailed as well. This feat can be accomplished in a number of ways: either by rendering the server-side form as opposed to the DataGrid, or by creating a new Page and server-side form, and rendering those. For a much more in-depth look at ASP.NET server control building, be sure to consider picking up a copy of Developing Microsoft ASP.NET Server Controls and Components.

    Happy Programming!

  • By Scott Mitchell (with input and ideas from Andy Smith)

    A Follow-Up Article is Available!
    If you try out the code discussed in this article in an ASP.NET 2.0 application, you may wind up with an exception that reads: "RegisterForEventValidation can only be called during Render();" If so, check out Emailing the Rendered Output of an ASP.NET Web Control in ASP.NET 2.0 for a discussion on why this error arises along with a couple of workarounds.



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