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.