Dynamically adding user controls registered in web.config - asp.net

I'm working on a project that has all its user controls registered in its web.config file (which seems very clean and tidy)
So far so good (here comes the problem) however I'm trying to dynamically create and add user controls to a page. These user controls fire events that need handling.
Ordinarily that wouldn't be a problem:
You just register the control in the page, load the control, cast it to the correct type, and assign the event handlers, add it to the page, sit back and let the magic happen, easy peasy.
But I can't reference the control's type when the control is registered in the web.config, which means no cast, which means no event handling!
Weirdly you can reference the type if you add the usercontrol to the page at design time!
There must be a way round this (without having to register the control on the page, or add a control at design time), what on earth am I missing?

It's been a while, but I think I've seen this type of behavior in ASP.NET when a project is a Web Site and not the Web Application. As far as I remember, the Web Site compiles each page into its own assembly and with no common name space and regardless of config requires the <%# Register %> directive. If you don't, you get the exact error of missing an assembly reference.
I would have to test to be sure...

By saying : "you can reference the type if you add the usercontrol to the page at design time"
Do you mean it adds an <%# Register %> Directive at the top of the page ?
Or maybe, it adds a using / Imports (depending on you using c# / vb.net) clause in your source document ?
Because, to be able to cast to your control type, you normally need to import the namespace in the codebind. Maybe this is just what is missing.

The <controls> section in web.config and the <%# Register %> directive are the same thing (with the small exception that entries in web.config apply to the whole application). They allow you to add design-time controls to a web form.
If you want to add controls to a page dynamically, use the LoadControl function to get an instance of your control. Given a control with a class name of "Header", the following will load a control, set a property, and add the control to the form named, "form1":
Dim head As Header = LoadControl("~/Controls/Header.ascx")
head.Text = "Some text..."
Me.form1.Controls.Add(head)

Related

ASP.NET User Control instance is null when referenced on Page_Load on page code behind

I have a user control that I have written and have added to an ASP.NET page, and functions just fine. However, I am trying to reference a property in the that custom control from code behind, on Page_Load, but cannot, because the variable, which is accessible, for the instance is null.
Is this normal for user controls and Page_Load? And if so, how can I make a reference to the control's instance in order to access its public properties? This is something I need to do before the page is rendered, in order to initialize some variables.
I had the same issue, and it turned out that I was registering my custom control incorrectly.
Correct Definition:
<%# Register Src="PeriodControl.ascx" TagName="PeriodControl" TagPrefix="ucs" %>
Incorrect Definition:
<%# Register TagPrefix="ucs" Namespace="MyWebsite" Assembly="MyWebsite" %>
The only difference was to reference the ascx file directly instead of the control in the assembly. Go figure!?
You can probably access your user control from the Page_PreRender event.
You can find more documentation about the page life cycle in asp.net here.

How are custom controls instantiated in asp.net?

My understanding is that when a component or custom control is defined in an aspx page using the <%Register%> tag, it is declared in an auto-generated designer.cs (C#) file by the compiler. If this custom control is never used in the aspx page, does this still happen in the designer.cs file?
Assuming the control is used in the aspx page, what mechanism then instantiates this control, how is it new-ed up behind the scenes? The designer file only declares it. Thanks much, and if there are good articles out there discussing this I’d be happy to read them.
This may not answer all your questions, but some of them. Rick Strahl wrote a great article a while back on compilation and deployment that describes how it works:
Compilation and Deployment in ASP.NET 2.0
I added part of the article that I think relates most to your question below:
Referencing other Pages and Controls
Remember that page and control compilation happens on a per directory basis! So referencing other pages and controls becomes a little more tricky for ASP.NET 2.0, because you can no longer assume that a CodeBeside class from another page or control is available in the current assembly. At best all pages and controls in the same directory end up in the same assembly, at worst each page or control gets its own assembly and they know nothing about each other.
If you need to reference another page from a control or another page you need to explicitly import it with the #Reference directive. Again this is different than ASP.NET 1.1 where all CodeBehind classes were immediately available to your entire Web application. In ASP.NET 2.0 an explicit assembly reference is required to load it.
Assume for a minute that you have the DataEntry.aspx page I showed earlier and you want to create a second page that uses the same CodeBeside class so you can reuse the page logic, but change the page layout in DataEntry2.aspx by changing a few colors and moving around the controls of the page. In essence you want to have two ASPX pages reference the same CodeBeside file.
Here’s how to do this:
<%# Reference Page="~/DataEntry.aspx" %>
<%# Page Language="C#" AutoEventWireup="true" Inherits="DataEntry" %>
I’m leaving out the CodeFile attribute reference the CodeBeside class of the DataEntry page, and add the #Reference tag to the page to force the CodeBeside class to be imported.
The same is true with any User Control definitions. To import a user control you need to use the #Register tag, which imports the assembly that the control lives in. ASP.NET is smart during compilation and figures out exactly where related assemblies live based on how the project is compiled. If the control or page lives in the same assembly no reference is actually added. But if it is external – in another directory for example, then the assembly reference is added.
Referencing problems
If you can explicitly reference other pages and controls in your markup pages, then all works well and as expected. But if you dynamically load controls or reference pages dynamically in your code, things get a lot more complicated.
The most common problem I run into is dynamic loading of controls. In ASP.NET 1.x you might have run code like this for dynamically loading controls into a page:
public partial class DynamicControlLoading : System.Web.UI.Page
{
protected CustomUserControl MessageDisplay = null;
protected void Page_Load(object sender, EventArgs e)
{
MessageDisplay = this.LoadControl( "~/UserControls/CustomUserControl.ascx") as CustomUserControl;
this.Controls.Add(MessageDisplay);
}
protected void btnSay_Click(object sender, EventArgs e)
{
this.MessageDisplay.ShowMessage(this.txtMessage.Text);
}
}
CustomUserControl in this case is a simple User Control that lives in another directory and is loaded dynamically at runtime. Further assume that you truly dynamically want to load this control so you may have a choice of several controls, or the end-user might even create a custom control that gets dropped into place instead.
If you run the code above in ASP.NET 2.0 it will likely fail. I say likely because there are some inconsistencies that will sometimes pick up control references automatically, for example if the user control lives in the same directory and gets compiled into the same assembly as the page, or if another page has the control referenced.
It should and usually will fail. Why? Because ASP.NET compiles on a directory level and the CustomUserControl lives in a separate directory and so goes into a separate assembly. It’s not visible to page class to get a strongly typed reference. Intellisense will show a big, fat and red exclamation point or nothing at all for the MessageDisplay control. When you run the page it will bomb.
You can reference the control as the Control type of course, but if you need to access any custom properties on the user control beyond Control properties you can’t unless you resort to Reflection. As far as I know there’s no way to add a reference to another user control or page programmatically because the reference needs to be available way earlier at compile time before your code ever runs.
Alternatives are to not load controls dynamically or at least provide some mechanism to load up any user controls beforehand on a page with the appropriate #Register tags. But that’s not always possible. The other option is to create a user control base class in APP_CODE and expose the public interface there. The main problem with this is that this base class will not have access to any internal controls of the user control and so the base class would have to use FindControl to reference any embedded controls. So this is inefficient as hell, and cumbersome to boot.
I’ve run into similar situations with inheritance scenarios. For example, inheriting one master page off another’s CodeBeside class. All works well, but the ASP.NET compiler complains that the Profile object is being overridden illegally (a compiler warning). Running with the inherited master page works, but there are quirks. User Controls added to the master page often fail with type conflicts as ASP.NET treats the user control added to the base page as a different type than the user control added to the second page.
It’s inconsistencies like these that deal with referencing other types that have made me waste an incredible amount of time, thinking I had something fixed only to find out later that it didn’t actually work consistently when I changed a completely different page. Worse you have to really understand the model to get your head around what might be wrong.
Bottom line: The overall ASP.NET 2.0 compilation model is internally complex. Most of the time you don’t need to understand it, but when you run into these boundary scenarios, you really DO have to understand what goes on behind the scenes to be able to work around the quirks.

using Caching in user control and VaryByControl

I have a user control in my asp.net application that load it's data from cache. i want it's cache be updated when the value of a variable be changed so i put this code on page directive
<%# OutputCache Duration="1000" VaryByParam="none"
VaryByControl="visitIsAu" %>
it works fine when i change the value of variable in markup like this
visitIsAu="true"
but it doesn't work when the value is changed through code behind like this
visitIsAu="<%=this.CurentUser.IsAuthorizedToVisitFiltered%>"
the value of variable does not change so its cache is not updated.
does anyone know why this happen?
For some reason I've never trusted the way ASP.NET handles cache using markup settings or using "injected" server tags on the aspx files. I know it works but it's only easy to control on simple scenarios.
I always implement cache control, when using user controls, at code behind level and taking into consideration that page life cycle that may have some catches base on the level of controls I have a certain page.
We can have: ascx > aspx or ascx > aspx > master.
In your case you need to guarantee that CurrentUser is set prior to the load of your user control. Check that using debug.

Use user control in the same folder as the page

I get this message at runtime of ASP.NET 2 page :
The page 'MyFolder/blabla.aspx' cannot use the user control 'MyFolder/MyControl.ascx', because it is registered in web.config and lives in the same directory as the page.
Of course I can separate them to 2 different folders and thus solve the problem, but the question is :
WTF !?!?! Why I can't put them in the same folder ?!
Why can't they all .. get along !?! :)
Thanks
This limitation is by design due to an internal design consideration re: performance.
See here for further info.
Remarks
The TagPrefixInfo class allows you to programmatically access and
modify tag-prefix information stored in a configuration file. It
provides the same functionality as the ASP.NET #Register
directive. Tag prefixes associate a "namespace" in ASP.NET to the
assemblies and namespaces that must be included for custom controls
and user controls to work properly. TagPrefixInfo objects are stored
as members of a TagPrefixCollection object. The
TagPrefixCollection class allows you to programmatically access
and modify the controls subsection of the pages section of a
configuration file.
TagPrefixInfo objects are added to the collection using the add
element and specifying a value for the tagPrefix attribute along
with values for other relevant attributes. The other required
information varies based on the kind of control you will use with the
specified tag prefix:
If it is a user control, you must define the TagPrefix, TagName, and Source properties.
If it is a custom control, you must define the TagPrefix, Namespace, and Assembly properties. The Assembly
property is not required if the control is in the application code
directory. The same tagPrefix value can be used to map to multiple
assemblies or namespaces.
Note When a source is specified, the user control itself must not be in the same directory as the page. If it is, you get a run-time
error when you attempt to load the page.
If you register it in the page or user control instead of the web.config it will load properly.
Add the following to the top of the page.
<%# Register TagPrefix="MyControlTagPrefix" TagName="MyControlTagName" Src="~/MyFolder/MyControl.ascx" %>
I suspect you could do it without registering it, if it was essential.
You could probably have a PlaceHolder and then use .Controls.Add(LoadControl("path.ascx"))
But if it's not essential then put it in a different directory due to the reasons #Barry says.
The answer can be found inside the .NET Framework Reference Source code for System.Web.dll:
https://referencesource.microsoft.com/#System.Web/UI/TagNameToTypeMapper.cs,ced01a48dbbb9f9c
TryUserControlRegisterDirectives
Don't allow a config registered user control to be used by a page that lives in the same directory, to avoid circular references. More specifically, this would break batching because our simple dependency parser would not be able to detect the implicit dependency triggered by the presence of a tag (VSWhidbey 165042).

Getting namespace name not found for ASP.net user control

So I'm having problems when I try to publish the website.
I'm in visual studio 2008 sp1.
I've got a bunch of user controls and on a few pages I'm using them programatically.
I've got a reference on the aspx page
<%# Reference Control="~/UserControls/Foo.ascx" %>
Then on the code behing I use
ASP.usercontrols_foo newFoo control = (ASP.usercontrols_foo)Page.LoadControl("~/UserControls/Foo.ascx");
If I navigate to the page it works fine, but when I goto publish the website I get a compile time error.
Argh, I'm bleeding development hours on this same issue. Does anyone have a solution to this ?
BTW: It builds if you uncheck "Allow this precompiled site to be updatable" (Right-click on the website, choose Property Pages, MSBuild Option)
But I need the site to be updatable.
I had this same problem - actually, in my case I could get it to compile and build my website, but it would blow up spectacularly when doing an automated build.
Try replacing your code as follows:
ASP.usercontrols_foo newFoo control = (ASP.usercontrols_foo)Page.LoadControl("~/UserControls/Foo.ascx");
with
USERCONTROLS_Foo newFoo control = (USERCONTROLS_Foo)Page.LoadControl("~/UserControls/Foo.ascx");
(Capitalization will be based on how you capitalized your directory name/control name - but in either case should highlight appropriately).
Specify a namespace for user control (see Dynamically Load a user control (ascx) in a asp.net website ).
I've found a solution for it. If i'll declare controls in user defined namespace, then I can use controls directly without using ASP or referencing it into aspx page.
It may have something to do with the specific type not being available. Can you change the control reference so the type and cast just use the base Control class:
Control control = Page.LoadControl("~/UserControls/Foo.ascx");
Yes, I can cast it to Control. But then I lose direct access to the methods on the usercontrol.
I know that I can access the methods by reflecting into the control, and I've successfully done that but that's far from ideal to access all the other user controls on the site.
Also, the follow-up error is that it cant find the usercontrols that on the markup
<%# Register src="CategoryRows.ascx" tagname="CategoryRows" tagprefix="abc" %>
<abc:CategoryRows ID="CategoryRows" runat="server" />
Note that I can run the site successfully both locally and on the server if I essentially XCopy the entire site (source code and all). But Publish fails.
Casting the user control may create many problem .my approach is to create a class (say control Class) put all the properties and method you need after casting and inherit this class from System.Web.UI.UserControl .Then in your user cotrol code file instead of System.Web.UI.UserControl user this control class .
now when ever you need casting cast with this class only . it will be light casting as well.
Please check:
Do you have any website.publishproj file?
If it exists, then delete and try again and publish code.

Resources