Instantiate/Load UserControl from DLL

S

Sascha

Hi there,

I searched carefully through the web before finally deciding to post
this message, because I could not find a solution for my problem.
Hopefully someone will have a hint or explanation for me! I apologize
for the length of this posting, but I wanted to make sure that I get an
answer other than "Hey man, just use LoadControl!", because this is not
what I want.

The Task: Isolate a collection of web forms which are created as
UserControls from the web application framework so that they can be
easily changed, exchanged, removed, and added, without having to
restart or recompile the web application.
My Approach: Store UserControls in separate project and dynamically
load assembly. Example code in C#:

[... Code Snippet Begin ...]
ObjectHandle PackageManagerHandle;
IPackageManager packageManager;

// Load Assembly
PackageManagerHandle = Activator.CreateInstanceFrom("Packages.dll",
"Packages.PackageManager");

// Unwrap type
packageManager = (IPackageManager) PackageManagerHandle.Unwrap();
UserControl control = packageManager.GetControl(ctrlName);
control.InitializeAsUserControl(this.Page);
[... Code Snippet End ...]

This is pretty much the same code that can be found in MSDN when
looking at Assembly.LoadFrom or Activator.CreateInstance. The
interesting part are the last two lines, where the actual UserControl
instance is created. The very last line came from the very informative
article at http://aspalliance.com/399 about LoadControl and
LoadTemplate, but actually does not change much for me.

The Result: When I execute the above code, it works fine and I get an
instance of my UserControl, but all the controls inside of the
UserControl are not initialized, therefore it shows up on the page as
an empty control. This seems to make sense, though, because the
instantiation only creates an instance of the UserControl class, but it
does not execute/interpret the actual ascx file that has the HTML code
in it.

Why not use Page.LoadControl? Here is why: First of all, LoadControl
expects a virtual path to the ascx file of the UserControl. This means
the ascx file has to be inside of the web root of that web application,
and I cannot compile it into my single DLL, which would be much nicer
to deploy and maintain.

While trying different things out, I found two solutions, which I
briefly want to present here:

Solution 1: Go back to using Page.LoadControl, but still call it from
my PackageManager class which is in another project (or solution, for
that matter) than my web application.
Advantage: It works!
Disadvantage: I need to have my user control files somewhere in the web
root of the application, otherwise LoadControl cannot find them. Also,
the type the UserControl inherits from needs to be known to the
assembly, otherwise I get an ugly Cannot Load Type error. As I said, it
works, but I was hoping for a more elegant solution.

Solution 2: While trying to overcome the fact that simply instantiating
the UserControl does not evaluate the ascx file I thought about using
ParseControl, which I usually don't like very much since it does not
compile or interpret any code. However, in this case this is exactly
what I want, because I already have my UserControl instance and all I
need are the controls inside of my UserControl instantiated with the
parameters specified in the HTML tags in the ascx file.
Here is how it works: First I create the UserControl instance, then I
use ParseControl to parse the ascx file into a control. Next I add this
parsed control to my user control. Here comes the tricky part:
Unfortunately I don't know what exactly is going on behind the scenes
if you instantiate a UserControl just by dropping it on to a page in
Visual Studio, but the key part is that ASP.NET connects the controls
in the ascx file with the protected member variables in the code
behind. This connection is somehow lost in the way I do it, so that
trying to access a server control from the code behind causes a Object
Not Set To An Instance exception.
I found a solution for this as well: By using Reflection I iterated
over the Fields collection of the UserControl and then used FindControl
to find the respective server controls in the Controls collection. This
way I could reconnect the code behind with the "code in front".
This is a short code snippet which illustrates this solution:

[... Code Snippet Begin ...]
StreamReader sr = new StreamReader(fileName);
Control parseControl = Container.Page.ParseControl(sr.ReadToEnd());
sr.Close();

userControl.Controls.Add(parseControl);
Type type = userControl.GetType();
System.Reflection.FieldInfo[] fields =
type.GetFields(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.DeclaredOnly);
foreach ( System.Reflection.FieldInfo field in fields )
{
Control ctrl = FindControlQ(userControl, field.Name);
field.SetValue(userControl, ctrl);
}
[... Code Snippet End ...]

The first three lines read the ascx file through a stream reader. This
has the HUGE advantage of being able to put the UserControl files
anywhere, not necessarily inside of the web root of the web
application.
Note that I am using FindControlQ which is a recursive version of
FindControl, so that controls nested in other NamingContainers are
found as well.


Despite the fact that I have actually two working solutions now, I am
left with two questions:
1. Is there an easier way to do this? The first code snippet at the
beginning of this posting seems to do *almost* what I want, so maybe I
am just missing a simple call which would save me all the ParseControl
and Reflection work? All I am doing is more or less mimicing the
behavior of LoadControl, so if there was a different way to execute
LoadControl on a file outside of the web root, that would work fine for
me.
2. Are there any reasons why going down that ParseControl/Reflection
path might cause serious problems later? I tested my solution with a
couple of UserControls, and it seems that ViewState, Validators etc.
are still working correctly, and my events in the code behind are
firing as I expect it.


Thanks for reading through all this, I'd be happy about feedback, and
if someone out there happens to find a solution for his or her problem
in this posting, feel free to ask any questions, I can also post more
code if necessary!

Thanks,
Sascha
 
M

msteller

Would you mind giving out the code for FindControlQ?
*Hi there,

I searched carefully through the web before finally deciding to post
this message, because I could not find a solution for my problem.
Hopefully someone will have a hint or explanation for me!
apologize
for the length of this posting, but I wanted to make sure that I ge
an
answer other than "Hey man, just use LoadControl!", because this i
not
what I want.

The Task: Isolate a collection of web forms which are created as
UserControls from the web application framework so that they can be
easily changed, exchanged, removed, and added, without having to
restart or recompile the web application.
My Approach: Store UserControls in separate project and dynamically
load assembly. Example code in C#:

[... Code Snippet Begin ...]
ObjectHandle PackageManagerHandle;
IPackageManager packageManager;

// Load Assembly
PackageManagerHandle = Activator.CreateInstanceFrom("Packages.dll",
"Packages.PackageManager");

// Unwrap type
packageManager = (IPackageManager) PackageManagerHandle.Unwrap();
UserControl control = packageManager.GetControl(ctrlName);
control.InitializeAsUserControl(this.Page);
[... Code Snippet End ...]

This is pretty much the same code that can be found in MSDN when
looking at Assembly.LoadFrom or Activator.CreateInstance. The
interesting part are the last two lines, where the actua
UserControl
instance is created. The very last line came from the ver
informative
article at http://aspalliance.com/399 about LoadContro
and
LoadTemplate, but actually does not change much for me.

The Result: When I execute the above code, it works fine and I ge
an
instance of my UserControl, but all the controls inside of the
UserControl are not initialized, therefore it shows up on the pag
as
an empty control. This seems to make sense, though, because the
instantiation only creates an instance of the UserControl class, bu
it
does not execute/interpret the actual ascx file that has the HTM
code
in it.

Why not use Page.LoadControl? Here is why: First of all, LoadControl
expects a virtual path to the ascx file of the UserControl. Thi
means
the ascx file has to be inside of the web root of that we
application,
and I cannot compile it into my single DLL, which would be muc
nicer
to deploy and maintain.

While trying different things out, I found two solutions, which I
briefly want to present here:

Solution 1: Go back to using Page.LoadControl, but still call i
from
my PackageManager class which is in another project (or solution
for
that matter) than my web application.
Advantage: It works!
Disadvantage: I need to have my user control files somewhere in th
web
root of the application, otherwise LoadControl cannot find them
Also,
the type the UserControl inherits from needs to be known to the
assembly, otherwise I get an ugly Cannot Load Type error. As I said
it
works, but I was hoping for a more elegant solution.

Solution 2: While trying to overcome the fact that simpl
instantiating
the UserControl does not evaluate the ascx file I thought abou
using
ParseControl, which I usually don't like very much since it does not
compile or interpret any code. However, in this case this is exactly
what I want, because I already have my UserControl instance and al
I
need are the controls inside of my UserControl instantiated with the
parameters specified in the HTML tags in the ascx file.
Here is how it works: First I create the UserControl instance, the
I
use ParseControl to parse the ascx file into a control. Next I ad
this
parsed control to my user control. Here comes the tricky part:
Unfortunately I don't know what exactly is going on behind th
scenes
if you instantiate a UserControl just by dropping it on to a page in
Visual Studio, but the key part is that ASP.NET connects th
controls
in the ascx file with the protected member variables in the code
behind. This connection is somehow lost in the way I do it, so that
trying to access a server control from the code behind causes a
Object
Not Set To An Instance exception.
I found a solution for this as well: By using Reflection I iterated
over the Fields collection of the UserControl and then used
FindControl
to find the respective server controls in the Controls collection.
This
way I could reconnect the code behind with the "code in front".
This is a short code snippet which illustrates this solution:

[... Code Snippet Begin ...]
StreamReader sr = new StreamReader(fileName);
Control parseControl = Container.Page.ParseControl(sr.ReadToEnd());
sr.Close();

userControl.Controls.Add(parseControl);
Type type = userControl.GetType();
System.Reflection.FieldInfo[] fields =
type.GetFields(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.DeclaredOnly);
foreach ( System.Reflection.FieldInfo field in fields )
{
Control ctrl = FindControlQ(userControl, field.Name);
field.SetValue(userControl, ctrl);
}
[... Code Snippet End ...]

The first three lines read the ascx file through a stream reader.
This
has the HUGE advantage of being able to put the UserControl files
anywhere, not necessarily inside of the web root of the web
application.
Note that I am using FindControlQ which is a recursive version of
FindControl, so that controls nested in other NamingContainers are
found as well.


Despite the fact that I have actually two working solutions now, I
am
left with two questions:
1. Is there an easier way to do this? The first code snippet at the
beginning of this posting seems to do *almost* what I want, so maybe
I
am just missing a simple call which would save me all the
ParseControl
and Reflection work? All I am doing is more or less mimicing the
behavior of LoadControl, so if there was a different way to execute
LoadControl on a file outside of the web root, that would work fine
for
me.
2. Are there any reasons why going down that ParseControl/Reflection
path might cause serious problems later? I tested my solution with a
couple of UserControls, and it seems that ViewState, Validators etc.
are still working correctly, and my events in the code behind are
firing as I expect it.


Thanks for reading through all this, I'd be happy about feedback,
and
if someone out there happens to find a solution for his or her
problem
in this posting, feel free to ask any questions, I can also post
more
code if necessary!

Thanks,
Sascha *
Would you mind giving out the code for FindControlQ?
 
F

Franz Thomsen

Did you manage to find out a solution?
I got exactly the same problem

Best Regards
Franz Thomsen
*Hi there,

I searched carefully through the web before finally deciding to post
this message, because I could not find a solution for my problem.
Hopefully someone will have a hint or explanation for me! I
apologize
for the length of this posting, but I wanted to make sure that I get
an
answer other than "Hey man, just use LoadControl!", because this is
not
what I want.

The Task: Isolate a collection of web forms which are created as
UserControls from the web application framework so that they can be
easily changed, exchanged, removed, and added, without having to
restart or recompile the web application.
My Approach: Store UserControls in separate project and dynamically
load assembly. Example code in C#:

[... Code Snippet Begin ...]
ObjectHandle PackageManagerHandle;
IPackageManager packageManager;

// Load Assembly
PackageManagerHandle = Activator.CreateInstanceFrom("Packages.dll",
"Packages.PackageManager");

// Unwrap type
packageManager = (IPackageManager) PackageManagerHandle.Unwrap();
UserControl control = packageManager.GetControl(ctrlName);
control.InitializeAsUserControl(this.Page);
[... Code Snippet End ...]

This is pretty much the same code that can be found in MSDN when
looking at Assembly.LoadFrom or Activator.CreateInstance. The
interesting part are the last two lines, where the actual
UserControl
instance is created. The very last line came from the very
informative
article at http://aspalliance.com/399 about LoadControl
and
LoadTemplate, but actually does not change much for me.

The Result: When I execute the above code, it works fine and I get
an
instance of my UserControl, but all the controls inside of the
UserControl are not initialized, therefore it shows up on the page
as
an empty control. This seems to make sense, though, because the
instantiation only creates an instance of the UserControl class, but
it
does not execute/interpret the actual ascx file that has the HTML
code
in it.

Why not use Page.LoadControl? Here is why: First of all, LoadControl
expects a virtual path to the ascx file of the UserControl. This
means
the ascx file has to be inside of the web root of that web
application,
and I cannot compile it into my single DLL, which would be much
nicer
to deploy and maintain.

While trying different things out, I found two solutions, which I
briefly want to present here:

Solution 1: Go back to using Page.LoadControl, but still call it
from
my PackageManager class which is in another project (or solution,
for
that matter) than my web application.
Advantage: It works!
Disadvantage: I need to have my user control files somewhere in the
web
root of the application, otherwise LoadControl cannot find them.
Also,
the type the UserControl inherits from needs to be known to the
assembly, otherwise I get an ugly Cannot Load Type error. As I said,
it
works, but I was hoping for a more elegant solution.

Solution 2: While trying to overcome the fact that simply
instantiating
the UserControl does not evaluate the ascx file I thought about
using
ParseControl, which I usually don't like very much since it does not
compile or interpret any code. However, in this case this is exactly
what I want, because I already have my UserControl instance and all
I
need are the controls inside of my UserControl instantiated with the
parameters specified in the HTML tags in the ascx file.
Here is how it works: First I create the UserControl instance, then
I
use ParseControl to parse the ascx file into a control. Next I add
this
parsed control to my user control. Here comes the tricky part:
Unfortunately I don't know what exactly is going on behind the
scenes
if you instantiate a UserControl just by dropping it on to a page in
Visual Studio, but the key part is that ASP.NET connects the
controls
in the ascx file with the protected member variables in the code
behind. This connection is somehow lost in the way I do it, so that
trying to access a server control from the code behind causes a
Object
Not Set To An Instance exception.
I found a solution for this as well: By using Reflection I iterated
over the Fields collection of the UserControl and then used
FindControl
to find the respective server controls in the Controls collection.
This
way I could reconnect the code behind with the "code in front".
This is a short code snippet which illustrates this solution:

[... Code Snippet Begin ...]
StreamReader sr = new StreamReader(fileName);
Control parseControl = Container.Page.ParseControl(sr.ReadToEnd());
sr.Close();

userControl.Controls.Add(parseControl);
Type type = userControl.GetType();
System.Reflection.FieldInfo[] fields =
type.GetFields(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic |
System.Reflection.BindingFlags.DeclaredOnly);
foreach ( System.Reflection.FieldInfo field in fields )
{
Control ctrl = FindControlQ(userControl, field.Name);
field.SetValue(userControl, ctrl);
}
[... Code Snippet End ...]

The first three lines read the ascx file through a stream reader.
This
has the HUGE advantage of being able to put the UserControl files
anywhere, not necessarily inside of the web root of the web
application.
Note that I am using FindControlQ which is a recursive version of
FindControl, so that controls nested in other NamingContainers are
found as well.


Despite the fact that I have actually two working solutions now, I
am
left with two questions:
1. Is there an easier way to do this? The first code snippet at the
beginning of this posting seems to do *almost* what I want, so maybe
I
am just missing a simple call which would save me all the
ParseControl
and Reflection work? All I am doing is more or less mimicing the
behavior of LoadControl, so if there was a different way to execute
LoadControl on a file outside of the web root, that would work fine
for
me.
2. Are there any reasons why going down that ParseControl/Reflection
path might cause serious problems later? I tested my solution with a
couple of UserControls, and it seems that ViewState, Validators etc.
are still working correctly, and my events in the code behind are
firing as I expect it.


Thanks for reading through all this, I'd be happy about feedback,
and
if someone out there happens to find a solution for his or her
problem
in this posting, feel free to ask any questions, I can also post
more
code if necessary!

Thanks,
Sascha *
 
Joined
Jul 27, 2007
Messages
1
Reaction score
0
Would something like the following work ?

Assembly assembly = Assembly.LoadFrom(@"C:\Dev\Examples\Examples\TabPluginHost\bin\App_Web_test2control.ascx.94427c25.dll");

Type controlType =assembly.GetType("ASP.Test2Control");

object control = Activator.CreateInstance(controlType);

cTab1Placeholder.Controls.Add((Control)control);



The example able uses a fixed class name 'ASP.Test2Control', but you could either iterate through all the classes in the DLL and instantiate each, or possibly using some naming convention "Control*"
 
Joined
Mar 12, 2008
Messages
1
Reaction score
0
Anyone have the solution?

Just want to bring this thread up.

I want to develop the ASCX control as same way as in website project but in class library (single assembly) instead. It is very simple idea but very difficult to find the solution. I think it should have some easy solution for that, if anyone can help.

Finally I found this thread, and found that what Sascha asked is what I want to ask, and what I have did was nearly same what he did.

Anybody have other solution for this?

Thanks,
NonGT
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,567
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top