Rendering a user control in html in code-behind

This is an article about rendering a control in code-behind page in the html content of the page. This kind of requirements are usually needed while creating a CMS. Please have a look, you will find it useful.

Sometimes, we come across a situation while creating a web site where we have to render a user control in our page. There are more than one methods available to use the user control in our page. Here in this article I am going to explain these methods. Please have a look.

I had recently faced a problem of rendering a user control in html text. I was working on CMS project, where the requirement was a html page whose contents were going to be added from admin section. And now the new requirement for the html page was a contact us section in the html page which would be created by dynamic controls depending on the page.

For example, I had a Event Page. So the admin had created a dynamic form regarding the controls related to event like, Event Name (TextBox), Event  Date (TextBox with Calendar), Event Location(TextBox). So, to solve the issue, we used the following method to render the user control in html text.

public static string RenderUserControls(string strContent)
{
StringWriter myTwitterWriter = new StringWriter();
     HttpContext context = HttpContext.Current;
if (strContent.Contains("[cms:MyControl]"))
        {
Page pageHolder3 = new Page();
            string PhotoGalleryHtml = string.Empty;
            myTextWriter = new StringWriter();

UserControl controlLatest = (UserControl)pageHolder3.LoadControl("~/controls/MyControl.ascx");
            pageHolder3.Controls.Add(controlLatest);
            context.Server.Execute(pageHolder3, myTextWriter, false);
            PhotoGalleryHtml = myTextWriter.ToString();

strContent = strContent.Replace("[cms:MyControl]", PhotoGalleryHtml);
      }
      return strContent;
}

In the above method, we were initially loading the control in page holder and then converting it to string with the help of Context writer and string writer objects.  The above method works completely fine for the controls to display the data in data controls like Repeaters, DataGrid, DataList, Image Gallery etc.

But this method was useful only when our control would contain controls like images, text, labels and other data just to be displayed. The above method failed when we had an event in the control. We had a dynamic Save button and then we had added a click event of the save button. But when we clicked it gave the error. We had declare the button something like this,

Button button = new Button();
button.ID = "btnSubmit";
button.Click += new EventHandler(btnSubmit_Click);
button.Text = "Submit";

And the btnSubmit_Click event like this

public void btnSubmit_Click(object sender, EventArgs e)
{
Response.Write(txtName.Text);
}

But this thing didn’t work. We were not able to get the event called on clicking the button. So now the real problem starts. We asked the question on forum but as the requirement was too long to make someone understand, we could not get exact answer from the forum post. So we implemented another method using web service.  

Method 1:
In this method we made a use of web service and a object called Dictionary for collection. It’s a system generic collection object.

Dictionary<string, string> TextBoxIDCollection = new Dictionary<string, string>();

Here in this object we are using two string to be stored in collection, one is the name of the textbox, here we used “PromptText” to store the  name of it and the other string is used to store ClientID of the control. Here is the code to add the control

Table table = new Table();
div1.Controls.Add(table);
Dictionary<string, string> TextBoxIDCollection = new Dictionary<string, string>();

TableRow row = new TableRow();
table.Rows.Add(row);
TableCell cell1 = new TableCell();
row.Controls.Add(cell1);

Label lable = new Label();
cell1.Controls.Add(lable);
lable.ID = "lbl" + promptText.Trim().Replace(" ", "");
lable.Text = promptText.Trim() + " :";                 

TableCell cell2 = new TableCell();
row.Controls.Add(cell2);
TextBox text = new TextBox();
cell2.Controls.Add(text);
text.ID = "txt" + promptText.Trim().Replace(" ", "");
TextBoxIDCollection.Add(promptText.Trim().Replace(" ", ""), text.ClientID);
row.Controls.Add(cell2);

You can see that we are adding a value of textbox clientID and the promptText value to the collection object. We added same procedure for other controls like radio buttons and checkboxes also. And added the values to the collection object. Then we created a submit button. See the code for that.

TableRow row3 = new TableRow();
table.Rows.Add(row3);
TableCell cell5 = new TableCell();
row3.Controls.Add(cell5);
Button button = new Button();
cell5.Controls.Add(button);
button.ID = "btnSubmit";
button.OnClientClick = "submitMyForm();return false;";
button.Text = "Submit";

On the ClientClick of the submit button, we called javascript function submitMyForm(); which will internally call the method of webservice written in Service.cs. Here we are not calling the server side event of the submit button click. See the following javascript code for submitMyForm()

<script type="text/javascript" language="javascript">
    function submitMyForm() {
        if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate();
        if(Page_IsValid){
            <%= clientString %>
            WebService.Service.button_click(textKeyValue, OnSuccess, OnFailed);
        }
    }

    function OnSuccess(response) { alert(response); }
    function OnFailed() {alert("Failed"); }
</script>

In the above javascript function you can see that we are passing three values to the button_click method of the webservice. The actual method will only have single parameter i.e. textKeyValue. The other two parameters OnSuccess and OnFailed are the other two javascript function which will handle the response from the method.  The OnSuccess method will show the response string in alert message. And if the method failed, the function OnFailed will show the “Failed” message in alert message box.
Now here is the method button_click in the web service.

[WebMethod(true)]
public string button_click(object obj)
{
try
{
System.Reflection.MethodInfo method = (System.Reflection.MethodInfo)Context.Session["ControlSubmitInfo"];
return method.Invoke(Activator.CreateInstance((Type)Context.Session["ControlType"]), new object[] { obj }).ToString();
}
catch (Exception e)
{
}
return "failed";
}

In the above method, the system is invoking a method an returning the response, which is the value of the control. This method will intern invoke Submit  method of the user control which is just returning the string value from collection object.  The response we will get as an alert messagebox on the page. And if any error ocures, it will return “Failed” message to the function.
The submit method was registerd in the actual page where the control was being rendered. Also the two session values ControlSubmitInfo and ControlType were registered in the parent page. Here is the logic to render that control in the page in Page_Load event

protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
      {
       Page pageHolder4 = new Page();
            string htmlContext = string.Empty;
            StringWriter myTextWriter = new StringWriter();
            HttpContext context = HttpContext.Current;

            UserControl control = (UserControl)pageHolder4.LoadControl("~/Controls/MyControl.ascx");

           Type ControlType = control.GetType();
            System.Reflection.MethodInfo method = ControlType.GetMethod("Submit");
            Session.Add("ControlSubmitInfo", method);
             Session.Add("ControlType", ControlType);

             pageHolder4.Controls.Add(control);
            context.Server.Execute(pageHolder4, myTextWriter, false);
            htmlContext = myTextWriter.ToString();

            htmlContext = Regex.Replace(htmlContext, @"^.*\b(hidden)(.*)(__EVENTTARGET|__EVENTARGUMENT|__VIEWSTATE)\b.*$", "", RegexOptions.Multiline);            
            ltlTest.Text = htmlContext; // writting the whole rendered html to Literal control.             
        }
    }

Initiall the page will look like this



After clicking the submit button, I will get the response in alert message like this



So in this way, I got the server side method called from dynamically generated button in user control and I get that method called in my page, where this usercontrol was rendering.

Method 2:
Now we are going to see the other method to render the user control in the page.
This method is very simple to make in use. We made the code regular to create the controls in my usercontrol. Then also I wrote the server side click method and registered it with the submit button.
So we used iFrame in the control. And we replaced the iframe with the server tag inside the html like this,

ltlTest.Text = ltlTest.Text.Replace("[cms:MyControl]", "<IFRAME NAME='embeddedFrame' WIDTH='500px' HEIGHT='300px' border=0 frameborder=0 scrolling=no SRC='ControlPage.aspx' ></IFRAME>");

where ltlTest is asp Literal control. (similar to label).

For  this method we followed the following steps,  
1. Created a user control. Added the submit button click event and other controls to the page in placeholder control.
2. We put this user control in another aspx page called “ControlPage.aspx”.
3. Now whenever we find the server tag, “[cms:MyControl]”, we replaced it with the iFrame in each page of our application.

And the above method worked perfectly…!!

The second method is easy to understand and to implement.  You need to just follow the above steps.
So here are the two methods which we tried in our project to render the usercontrol with dynamic controls inside. Both the methods worked, but the method 2 worked more perfectly and its easy to implement.

Please find the code for first method as an attachment here.  Download_Code.

By Shailendrasinh Parmar   Popularity  (2822 Views)
Picture
Biography - Shailendrasinh Parmar