The inability of LoadControl to accept Constructor Parameters is a real pain in the donkey. This post tells you how to get around that hella annoying issue.
The awesome thing about ServerControls in ASP.NET is that you can instantiate them via a constructor, and hence offer a programming model similar to windows forms user controls. Unfortunately, you can't apply the same concept to UserControls because they are really mini pages within themselves. So you are limited to calling something like -
LoadControl("WebUserControl.ascx") ;
Wouldn't it be nice if you could instead do ...
LoadControl("WebUserControl.ascx",constructorparameter1, constructorparameter2, constructorparameter3 ...) ;
So say for instance, it would be nice if the following code would work -
Control toAdd = LoadControl("WebUserControl.ascx","Sahil Malik",5) ;
PlaceHolder1.Controls.Add(toAdd) ;
Note: There is a new overload new in .NET 2.0 which takes the signature LoadControl(Type,object[]), which is very similar to the above. But it is frequently problematic in ASP.NET 2.0 to refer to a user control in a strongly typed manner.
Well, here is how you'd make it work.
1. First of all write the user control (Duhh!!). My user control has a label on it called Label1, and it has the following code-behind.
public partial class WebUserControl : System.Web.UI.UserControl
{
public WebUserControl()
{
}
public WebUserControl(string labelText, int howManyTimes)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder() ;
for (int i = 1; i <= howManyTimes; i++)
{
sb.Append(labelText) ;
sb.Append("<br>") ;
}
Label1.Text = sb.ToString() ;
}
}
As you can see, I am simply appending the same text over and over again. Note that it is important to explicitly create a default public constructor (i.e. one without parameters) or you'd confuse the ASP.NET runtime.
2. Step #2 is to add the following private method to your default.aspx, or add it as a protected method to the base class of all your pages, or a static method that can somehow access your page object. In my case I kept things simple and added a private method to my default.aspx.
private UserControl LoadControl(string UserControlPath, params object[] constructorParameters)
{
List<Type> constParamTypes = new List<Type>() ;
foreach (object constParam in constructorParameters)
{
constParamTypes.Add(constParam.GetType()) ;
}
UserControl ctl = Page.LoadControl(UserControlPath) as UserControl;
// Find the relevant constructor
ConstructorInfo constructor = ctl.GetType().BaseType.GetConstructor(constParamTypes.ToArray()) ;
//And then call the relevant constructor
if (constructor == null)
{
throw new MemberAccessException("The requested constructor was not found on : " + ctl.GetType().BaseType.ToString()) ;
}
else
{
constructor.Invoke(ctl,constructorParameters) ;
}
// Finally return the fully initialized UC
return ctl;
}
3. And finally, add the following to the Page_Load (or any other suitable place) of your default.aspx -
protected void Page_Load(object sender, EventArgs e)
{
Control toAdd = LoadControl("WebUserControl.ascx","Sahil Malik",5) ;
PlaceHolder1.Controls.Add(toAdd) ;
}
Bingo, build the website and run, and here is what you see -

So there ya go, now you can call a UserControl pretty much like a ServerControl. Ok good, another major headache solved. :-)
On
9/3/2006 6:31:01 PM
Netflash
said ..
Great article! Since you know so weel hoe to work with UserControls, can you please give me some hints on how can i use embed images in a resource file of a UserControl that is compiled into an assembly. I can get text from the resource file but not images. With server control works well but with UC it doesn't seem to work. Thank you.
|
On
9/3/2006 9:57:14 PM
Sahil Malik
said ..
Well you can make it work using the System.Globalization namespace.
|
On
9/4/2006 9:08:43 AM
Netflash
said ..
I'm sorry, but can you explain what your idea is? Thank you.
|
On
9/4/2006 3:27:41 PM
Sahil Malik
said ..
Hmm .. (trying very hard to be polite) .. I'd love to break it down more, but then I'd be doin' your work eh? :)
Anyway, I'm gonna put this in my "will blog" queue and get to it when I can. Please don't mind my being busy. :)
|
On
9/8/2006 1:29:59 PM
Netflash
said ..
I don't know where that hostility came from. I just asked for some help. I didn't ask you to e-mail me a zip with the code done. You must admit that mentioning System.Globalization has the solution, it's kind of vague. If you don't have the time i understand but it would be enough to say that. Thank you anyway.
|
On
9/9/2006 11:00:38 AM
Sahil Malik
said ..
Hey Netflash -
There is no hostility :).
This is a constant challenge I face, and I don't have a solution to. (Please read: http://blah.winsmarts.com/Post.aspx?postID=67).
Yes I admit that mentioning Sys.g is vague, but I am also certain that a solution can be found there. Then it just boils down to spending time hitting that wall and figuring it out. At that point, I am not no advantage compared to you - except, I am doing your work for you. (And you = proverbial you, i.e. many others like you that contact me).
Yes I don't have the time, but I do want to help. I think my only answer is to stop the urge to reply to every comment I get on my blog. Maybe some other person in future will come by and answer the very same Q on the same post? If you have any suggestions to help this picture, I'm all ears - and there is no hostility.
|
On
9/21/2006 9:51:38 AM
Per Magne Bjornerud
said ..
I've been using this approach: One static method in the UserControl functioning as a surrogate constructor. Taking the calling object as a paramter, as this seems to be needed to load it properly.
public static MyControl ConstructFor(TemplateControl caller, object parameter) {
MyControl instance = (MyControl)caller.LoadControl("~/Path/MyControl.ascx");
instance.member = parameter;
return instance;
}
--
The the caller object would simply do this:
MyControl control1 = MyControl.ConstructFor(this);
|
On
1/6/2007 8:25:04 PM
Fritz Schenk
said ..
Thoroughly elegant!
|
On
1/29/2007 3:27:02 AM
Steve
said ..
Hi malik
Great solution. As I prefer coding in VB, how do i translate the line: -
List<Type> constParamTypes = new List<Type>() ;
Thanks in advance
|
On
5/12/2007 8:48:00 PM
Magni Hansen
said ..
Translated into vb
Dim constParamTypes As New List(Of Type)()
|
On
6/4/2007 3:09:30 AM
Pradeep
said ..
HOw to create user control in asp.net
|
On
7/16/2007 11:24:40 AM
teatime
said ..
Per Magne Bjornerud: most impressive, saved me a whole world of pain.
|
On
9/6/2007 4:38:32 PM
Timothy J Bridgemen
said ..
Sahil,
I'm just starting a project that involves creating user controls that I dynamically add to the page at run time that the user can modify. With the aid of this post I was able to initialize the controls with values after converting the code to VB.Net. What I'm missing is how do I get the values back out. Any help you can give would be appreciated.
Let me know VIA my Email when you answer.
Thank-you,
Tim
|
On
9/22/2007 6:07:24 PM
Raj
said ..
Any idea why I get this exception:
The requested constructor was not found on : " + ctl.GetType().BaseType.ToString())
Thank you,
Raj
|
On
11/27/2007 9:55:03 AM
Barret
said ..
Great article! Exactly what I was looking for -- Thanks!
|
On
12/4/2007 5:39:17 AM
halby
said ..
awesome mate!!!! i was looking for this code for couple of days!!!! thanks a lot
|
On
1/17/2008 7:34:58 AM
Bob Smith
said ..
Fantastic Article, Saved me ages!
|
On
5/4/2008 6:20:42 AM
Kevin
said ..
Could anyone help me to translate LoadControl functioin to VB ? I have tried it but failed. Really thanks a lot.
|
On
5/14/2008 8:44:42 PM
Ray
said ..
I am wondering of the WebUserControl.ascx is being loaded (executed) 2 times because of the code:
Page.LoadControl(UserControlPath) as UserControl;
and
constructor.Invoke(ctl, constructorParameters)
Is that true ?
|
On
6/10/2008 7:37:01 AM
Ken
said ..
Here is a dirty but working aproge:
Loading control:
Control _Control = LoadControl("PageItem.ascx");
_Control.SkinID = "1";
MyPage.Controls.Add(_Control);
And in the control just us (in Page_Load):
Label1.Text = this.SkinID;
|
On
9/10/2008 5:01:23 AM
Mark Morgan
said ..
Thanks for this article, it's helped me a lot, what I need to do is have a piece of UI and duplicate it onto multiple dynamically created tabs (using ASP.NET Ajax Toolkit), this technique I think will help me achieve that. For the vb.net developers out there I've converted the c# and got it working (not a lot of work on my part, but hopefully useful), again kudos to Sahil!
User Control in /Controls folder
WebUserControl.ascx
<%@ Control Language="VB" AutoEventWireup="false" CodeFile="WebUserControl.ascx.vb" Inherits="Controls_WebUserControl" %>
<asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
WebUserControl.ascx.vb
Partial Class Controls_WebUserControl
Inherits System.Web.UI.UserControl
Public Sub New()
End Sub
Public Sub New(ByVal labelText As String, ByVal howManyTimes As Integer)
Dim sb As New System.Text.StringBuilder()
For i As Integer = 1 To howManyTimes
sb.Append(labelText)
sb.Append("<br>")
Next
Label1.Text = sb.ToString()
End Sub
End Class
UCTest.aspx page :
<%@ Page Language="VB" AutoEventWireup="false" CodeFile="UCTest.aspx.vb" Inherits="UCTest" %>
<!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 runat="server">
<title>Untitled Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:PlaceHolder ID="PlaceHolder1" runat="server"></asp:PlaceHolder>
</div>
</form>
</body>
</html>
UCTest.aspx.vb
Imports System.Collections.Generic
Partial Class UCTest
Inherits System.Web.UI.Page
Private Overloads Function LoadControl(ByVal UserControlPath As String, ByVal ParamArray constructorParameters As Object()) As UserControl
Dim constParamTypes As New List(Of Type)()
For Each constParam As Object In constructorParameters
constParamTypes.Add(constParam.[GetType]())
Next
Dim ctl As UserControl = TryCast(Page.LoadControl(UserControlPath), UserControl)
' Find the relevant constructor
Dim constructor As Reflection.ConstructorInfo = ctl.[GetType]().BaseType.GetConstructor(constParamTypes.ToArray())
'And then call the relevant constructor
If constructor Is Nothing Then
Throw New MemberAccessException("The requested constructor was not found on : " + ctl.[GetType]().BaseType.ToString())
Else
constructor.Invoke(ctl, constructorParameters)
End If
' Finally return the fully initialized UC
Return ctl
End Function
Protected Sub Page_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
Dim toAdd As Control = LoadControl("~/Controls/WebUserControl.ascx", "Sahil Malik", 5)
PlaceHolder1.Controls.Add(toAdd)
End Sub
End Class
|
On
9/10/2008 10:23:47 AM
Sahil Malik
said ..
Thanks Mark. It is because people sometimes find what I write useful .. that I keep doing it. And thanks for contributing here.
Sahil
|
On
10/28/2008 7:29:09 PM
Nikolay Marinov
said ..
Sahil, great solution, thank you very much!!!
|
On
12/28/2008 8:04:01 PM
Pierre Buckley
said ..
Thank you for this solution. You are a genius my friend. I hope to one day have your skills with vb.net.
|
On
4/13/2009 1:32:40 PM
Renu
said ..
This code causes the page load of the user control to be fired twice. Is there any way this can be avoided?
|
On
4/30/2009 4:19:09 AM
James West
said ..
You absolutely are the man! This has worked an absolute treat for me!
Thank you
|
On
5/13/2009 3:25:16 PM
Bill Martin
said ..
Excellent solution. Very useful!
|
On
5/14/2009 3:41:03 PM
Steve Lydford
said ..
Great solution! Thanks very much.
|
On
7/22/2009 11:28:54 AM
Jacen Solo
said ..
Brilliant solution Sahil.
|
On
8/14/2009 10:34:34 AM
Tony
said ..
Thank you, sir. This is one of the rare times that I was able to find an explanation and code that specifically address an issue I was attempting to resolve.
|
On
9/29/2009 6:46:35 PM
Dr Omm
said ..
Thank you very much for this information. You save my life. I was looking for something like this for a long time ago. Please, still sharing you knowledge, you will go to the heaven...
|
On
4/29/2010 10:02:32 AM
zmrcic
said ..
I get Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. on
constructor.Invoke(ctl, constructorParameters) line
|
On
5/29/2010 5:26:20 PM
Laramie
said ..
I am also receiving System.Security.Permissions.SecurityPermission error in medium trust.
|
On
6/30/2010 3:56:50 AM
Isaac
said ..
I get the error that UserControl is namespace , but it is used as type.
Can you please help me out?
|
On
7/5/2010 6:56:51 AM
Ali
said ..
Extremely helpful - THANKS!
|
On
7/13/2010 5:34:59 PM
Amanda
said ..
Huge help. Thanks so much for this.
|
On
8/4/2010 11:04:03 AM
Zubair
said ..
Oh... Malik Jee tusee Gr8 ho....:)
You really made my day.
Thanks...
|