Wednesday, November 9, 2011

Dynamically create ASP.NET user control using ASP.NET Ajax and Web Service


In this post I will explain how to load ASP.NET user control dynamically using ASP.NET AJAX and Web Service. In next post I will explain the samething using JQuery. Lot of user asked me to mentioned examples in VB.NET, so in this article, I will explain code in both language C#/VB.NET.    

You can download the VB.NET solution code here and the C# solution codehere

To make web UI dynamic and more responsive to the various situations and modes, several techniques are used by the developers like making irrelevant controls invisible, disabling unused controls. This technique makes you page complex and heavy in size. Dynamic user control is another story. With dynamic control creation you will get the unmatched flexibility and innovation with robustness. Using this technique you can add any usercontrol to any page as per demand without postback or update panel.

I am using the last post example and extending it to use this feature. In last post I created the gridview control and placed on the page, In this example I will create this control on page dynamically using web service and ASP.NET Ajax and enhance it to support tooltip, mouseover, mouseout and click effect using ASP.NET Ajax client side framework. 

Below is the screenshot of the initial page. 
Gridview Effect 

When user clicks on "Load Customer Order", It will call the webservice get the usercontrol html data and load in the 'testDIV'. 

Gridview Effect 

Same way user can clicks on "load login" button to load "User Login" form.

Gridview Effect 

the XHTML code of the page in mentioned below.  

<%@ Page Language="C#" EnableViewState="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="head" runat="server">
    <title>With MS Ajax</title>
    <link type="text/css" href="StyleSheets/iGridView-classic.css"rel="stylesheet" />
    <link type="text/css" href="StyleSheets/iGridView-common.css"rel="stylesheet" />
    <style type="text/css">
        body
        {
            width:95%;
            padding-left:20px;
            font-family:Arial;
            font-size:10pt;
            padding-right:20px;
        }
    </style>
</head>
<body>
    <form id="form" runat="server">
        <asp:ScriptManager runat="server">
        <Services>
        <asp:ServiceReference Path="~/ScriptService.asmx" />
        </Services>
        </asp:ScriptManager>
        <input type="button" value="Load Customer Order"onclick="getData('~/Controls/GridView.ascx');" />
        <input type="button" value="Load Login"onclick="getData('~/Controls/LoginControl.ascx');" />
        <input type="button" value="Register New User"onclick="getData('~/Controls/NewUserControl.ascx');" />
        <div id="testDiv"></div>
    </form>
</body>
</html>

To use web service on page you need to mention it in ScriptManager. 

<asp:ScriptManager runat="server">
<Services>
<asp:ServiceReference Path="~/ScriptService.asmx" />
</Services>
</asp:ScriptManager>

Also update maxJsonLength in web.config otherwise you will get "maximum length exceed" exception for large json data. 

<system.web.extensions>
              <scripting>
                     <webServices>
        <jsonSerialization maxJsonLength="5000000" />
                     </webServices>
              </scripting>
       </system.web.extensions>


Below is the code of webservice file in C# with comment, don't forget to add
System.Web.Script.Services.ScriptService() attribute to mark this web service as script service.
using System;
using System.Collections;
using System.IO;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.Services;
using System.Web.Services.Protocols;

/// <summary>
/// Summary description for ScriptService
/// To access from ASP.NET AJAX remember to mentioned
/// System.Web.Script.Services.ScriptService() attribute
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService()]
public class ScriptService : System.Web.Services.WebService
{

    public ScriptService()
    {
        //Uncomment the following line if using designed components
        //InitializeComponent();
    }

    /// <summary>
    /// Get User Control Html
    /// use EnableSession=true if you are using session variables
    /// </summary>
    /// <returns>Html Table</returns>
    [WebMethod(EnableSession = true)]
    public string GetControlHtml(string controlLocation)
    {
        // Create instance of the page control
        Page page = new Page();

        // Create instance of the user control
        UserControl userControl = (UserControl)page.LoadControl(controlLocation);

        //Disabled ViewState- If required
        userControl.EnableViewState = false;
       
        //Form control is mandatory on page control to process User Controls
        HtmlForm form = new HtmlForm();

        //Add user control to the form
        form.Controls.Add(userControl);
       
        //Add form to the page
        page.Controls.Add(form);

        //Write the control Html to text writer
        StringWriter textWriter = new StringWriter();

        //execute page on server
        HttpContext.Current.Server.Execute(pagetextWriterfalse);
       
        // Clean up code and return html
        return CleanHtml(textWriter.ToString());
    }

    /// <summary>
    /// Removes Form tags
    /// </summary>
    private string CleanHtml(string html)
    {
        return Regex.Replace(html@"<[/]?(form)[^>]*?>""",RegexOptions.IgnoreCase);
    }

}

Same code of script service in VB.net is mentioned below

Imports System
Imports System.Collections
Imports System.IO
Imports System.Text.RegularExpressions
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.HtmlControls
Imports System.Web.Services
Imports System.Web.Services.Protocols
''' <summary>
''' Summary description for ScriptService
''' To access from ASP.NET AJAXremember to mentioned
''' System.Web.Script.Services.ScriptService() attribute
''' </summary>
<WebService([Namespace]:="http://tempuri.org/")> _
<WebServiceBinding(ConformsTo:=WsiProfiles.BasicProfile1_1)> _
<System.Web.Script.Services.ScriptService()> _
Public Class ScriptService
    Inherits System.Web.Services.WebService
    'Uncomment the following line if using designed components
    'InitializeComponent();
    Public Sub New()
    End Sub
    ''' <summary>
    ''' Get User Control Html
    ''' use EnableSession=true if you are using session variables
    ''' </summary>
    ''' <returns>Html Table</returns>
    <WebMethod(EnableSession:=True)> _
    Public Function GetControlHtml(ByVal controlLocation As StringAsString
        ' Create instance of the page control
        Dim page As New Page()
        ' Create instance of the user control
        Dim userControl As UserControl =DirectCast(page.LoadControl(controlLocation), UserControl)
        'Disabled ViewState- If required
        userControl.EnableViewState = False
        'Form control is mandatory on page control to process User Controls
        Dim form As New HtmlForm()
        'Add user control to the form
        form.Controls.Add(userControl)
        'Add form to the page
        page.Controls.Add(form)
        'Write the control Html to text writer
        Dim textWriter As New StringWriter()
        'execute page on server
        HttpContext.Current.Server.Execute(pagetextWriterFalse)
        ' Clean up code and return html
        Return CleanHtml(textWriter.ToString())
    End Function
    ''' <summary>
    ''' Removes Form tags
    ''' </summary>
    Private Function CleanHtml(ByVal html As StringAs String
        Return Regex.Replace(html"<[/]?(form)[^>]*?>""",RegexOptions.IgnoreCase)
    End Function

End Class
The javascript code to access web service with comment is mentioned below.
<script type="text/javascript">
        ///<summary>Method for Web Service Failed Event</summary>
        ///<param name="error">Error Object</param>
        ///<returns>false</returns>
        function onFailedWS(error)
        {
         alert(error.get_message());
        }
   
        ///<summary>This is the callback function that processes the Web Service return value.</summary>
        ///<param name="result">result Object from Web Service</param>
        function SucceededCallback(result)
        {
            //get the div element
            var RsltElem = $get("testDiv");
            //update div inner Html
            RsltElem.innerHTML =result;
            //format table inside the div
            //if you have multiple table call format table in loop
            formatTable(RsltElem.getElementsByTagName('table')[0].id)
        }
   
        ///<summary>This function calls the GetControlHtml Web Service method.</summary>
        ///<param name="error">Succeed Object</param>
        ///<param name="error">Error Object</param>
        function getData(controlLocation)
        {
                ScriptService.GetControlHtml(controlLocation,SucceededCallbackonFailedWS);
        }
       
        function formatTable(gvID){
            //get the gridview
            var gv = $get(gvID);
            //get numbers of column in grid view
            cols = gv.rows[0].cells.length - 1;
            //get numbers of rows in grid view
            rows = gv.rows.length -1;
            //intialized looping variables
            var i=0;
            var j=0;
            //loop for each row in gridview
            for(i=1;i<rows;i++){
                //attach mouseover event for row
                $addHandler(gv.rows[i], "mouseover"mouseOver);
                //attach mouseout event for row
                $addHandler(gv.rows[i], "mouseout"mouseOut);
                //attach click event for row
                $addHandler(gv.rows[i], "click"onClick);
                 //loop for each cell in row
                for(j=0;j<cols;j++){
                        //set tooltip for cells in row
                    setTitle(gv.rows[i].cells[j]);                        
                }
            }   
        }
        function setTitle(object){
            //check browser type
            if(Sys.Browser.name =="Firefox"){
            object.title = object.textContent;
            }
            else{
            object.title = object.innerText;
            }
        }
       
        function mouseOver(objectEvent){
            //is row already clicked
            if(!isClickedStyleSet(objectEvent.target.parentNode.className))
            {
            //set class name
            objectEvent.target.parentNode.className = "row-over" ;
            //check browser type
             if(Sys.Browser.name !="Firefox"){
                objectEvent.target.parentNode.style.cursor ="hand";
            }}
        }
        function mouseOut(objectEvent){
            //is row already clicked
            if(!isClickedStyleSet(objectEvent.target.parentNode.className)){
            //set class name
            objectEvent.target.parentNode.className = "data-row" ;
        }}
        function onClick(objectEvent){
            //set class name
            if(!isClickedStyleSet(objectEvent.target.parentNode.className))
            {
                objectEvent.target.parentNode.className = "row-select" ;
            }
            else
            {
                objectEvent.target.parentNode.className = "data-row" ;
            }
        }
        function isClickedStyleSet(className){
            //if row is already clicked return true
            if(className == "row-select"){
            return true;
            }
            return false;
        }
    </script>

To load any usercontrol you need to call "getdata" function with control location and name for example
getData("~/Controls/GridView.ascx");.
The benefit of the above approach are following
  • No need to the add usercontrol on the page at design time
  • Post back not required to load new User control 
  • Update panel not required
  • Developer/ User can add usercontrol to any web page
  • Developer can load control on demand 
  • It will reduce intial page size
  • Easy to create custom User interface
  • More Responsive as less data is loaded on page
In next post I will explain above approach with JQuery. Please post your valuable feedback for this article. 
You can download the VB.NET solution code here and the C# solution codehere