I am developing a composite control for an ASP.NET application and just by chance initially developed the control to use an ITemplate to create its child controls. There will be a series of buttons within the child controls that are to be handled in the root control. I am using the OnBubbleEvent with CommandName/CommandArgument properties on the buttons to wire it all up. And everything worked great... until I changed the templates to controls.
When I converted the template to a Control and called Controls.Add(new ChildControl()) in my root control instead of InstantiateIn(this), event bubbling no longer works.
Any idea why?
(Everything, and I mean everything, else is the same.)
Your composite control must implement INamingContainer interface in order to receive OnBubbleEvent calls. The CompositeControl class already implements this interface... in your case it stopped working not because of ITemplate thing, but because you changed inheritance from CompositeControl to Control.
See this: http://msdn.microsoft.com/en-us/library/system.web.ui.control.onbubbleevent.aspx
they say it there.
Related
I'm trying to create an ascx control that can wrap content like a panel. I'm looking to do something like -
<%# Register TagPrefix="FOO" TagName="Section" Src="CollapsibleSection.ascx" %>
<Foo:Section runat="server">
[ Section of asp.net webforms page ]
</Foo:Section>
It has been a while since I've done web forms and cannot remember if this is possible to do? While I could write a fully custom control, there are many advantages in my app if this is possible.
You need to decorate your user control with TemplateContainerAttribute and inherit from INamingContainer.
This is referred to as a templated control.
Here's a simple tutorial explaining how this is done in both C# and VB.Net: How to: Create Templated ASP.NET User Controls
Quoted from MSDN (link above):
In the .ascx file, add an ASP.NET PlaceHolder control where you want the template to appear.
In the user control's code, implement a property of type ITemplate.
Define a server control class that implements the INamingContainer interface as a container in which to create an instance of the
template. This is called the template's naming container.
Apply the TemplateContainerAttribute to the property that implements ITemplate and pass the type of the template's naming
container as the argument to the attribute's constructor.
In the control's Init method, repeat the following steps one or more times:
Create an instance of the naming container class.
Create an instance of the template in the naming container.
Add the naming container instance to the Controls property of the PlaceHolder server control.
I am writing a composite control in c# asp.net as an experiment and would like to have all the instances of my composite control interact with one external control. Is this possible?
Things I have tried:
option 1: check from within the control whether an external control exists on the page, and add it if it doesn't
option 2: have the target control's id passed to the composite control at design time and then use this.Page.FindControl()
Obviously it was wishful thinking that it would be that simple :)
If I try do this from within the CreateChildControls Method, this.Page.FindControl(target control) always returns null. If I try to add the control to the page from within this method, it throws an exception:
"The control collection cannot be modified during DataBind, Init, Load, PreRender or Unload phases."
is there another method / event where I can achieve this?
Why don't you expose a public property on your Composite Control of what output from them, then when rendering the Panel's contents, recurse through the page, find all instances of the composite control, grab the text, and add it to the panel?
You can create multiple instances on the same Web form by implementing the INamingContainer Interface. This basically helps prevent id clashes in the same namespace.
If you want to access another control set a property on it to expose the data you want made public.
Build Composite Controls
What is the correct event/method in the ASP.NET life cycle to dynamically add Child Controls ?
My objective is to ensure that all the input controls on a User Control have the correct associated Validator and Label controls, based on configuration from an external file.
It seems like the correct place should be either OnInit(EventArgs e) or CreateChildControls(). Both of them are behaving a little bit unexpected, and rather than try to debug each of them, I figured I'd first ask you guys which one (or other) to use.
Its OnInit, and you need to do it on first load and on post back.
Since this is a Web User Control (ASCX) create the dynamic controls during OnInit. By creating them during OnInit they will be created on the first page load and on every postback.
The CreateChildControls method is typically used for rendering in custom server controls.
I don't think I understand fully how ASP.NET does inheritance of controls.
I have a user control, ucBase, which has an asp.net label in the ascx file. Code behind references the label and it works fine during run time if the control is not a parent for another user parent.
If I have another user control, ucChild, inheriting from ucBase, the label in ucBase's code is always null. ucChild has no controls in its ascx file
The server controls (like the label) needs to be declared in the ascx file and not created programmatically.
What needs to be done for ucBase to see its own controls when it's a parent user control?
The issue:
Inheritance only inherits the code part of your user control. Markup is not something that can be inherited, since it is dynamically compiled at runtime.
The relationship between the markup and your code-behind is done through the .designer.cs partial class that comes with your user control. This designer file contains declarations for all the objects in your markup. This basically decorates the code-behind class with a bunch of fields that are null object references - these will be initialized with actual instances when the compiled markup code is run.
When you inherit from the .ascx file, you are inheriting all these null object placeholders. However, since the markup in your new control is not the same as the parent control, none of those objects are actually created when the new control's markup doesn't contain the corresponding markup, and when it is parsed and compiled, all the references stay null. Does this make sense?
The fix:
The best way to do this is to make your user controls self contained, i.e. favor code-based composition rather than markup based. In other words, instead of using markup, set up your user control using Page_Init and adding all the controls you need to the Controls collection in code behind.
Then when you inherit this class, all the same code will be executed, ensuring that your child usercontrol has the same UI controls in it.
I'm trying to avoid a composite control or using and ASCX by extending an existing control. However, I'm having trouble with getting the controls added to the inherited control and keep their view-state/post-back integrity. Whenever I add the controls during pre-render, the controls show up, but the post-back throws a viewstate exception. I tried adding them both there and during LoadViewState (which of course was a long-shot silly). Init is not available from the control which I'm extending.
The exception is
Sys.WebForms.PageRequestManagerServerErrorException:
Failed to load viewsstate. The control
tree into which viewstate is being
loaded must match the control tree
that was used to save viewstate during
the previous request. For example,
when adding controls dynamically, the
controls added during a post-back must
match the type and position of the
controls added during the initial
request
Actually, microsoft says you should override the CreateChildControls method.
You can call the base class method before or after you add the controls, I'm not sure if there is a convention there.
protected override void CreateChildControls(){
Controls.Add(someControl);
base.CreateChildControls();
}
Hope that helps!
You should add them in OnInit or in CreateChildControls. Anyway, to avoid having troubles with ViewState, read this GREAT article. Possibly, sample "4. Initializing child controls programmatically" is your case.