@ Forums: Templated composite controls
Asked on ASP.NET Forums
I am trying to build a templated composite control but I am having some problems with postbacks.
When I click Button1 and the event handler (Button1_Click) gets executed on the postback, Label1 is null... For some reason the CreateChildControls doesn't get called for Test2 when a postback occurs from Test1.... Any ideas
Here is the code for the control:
public class Test : CompositeControl
{
protected override void CreateChildControls()
{
Controls.Clear();
PlaceHolder p = new PlaceHolder();
if (Template != null)
{
TestTemplate f = new TestTemplate();
f.ID = "TestTemplate1";
Template.InstantiateIn(f);
p.Controls.Add(f);
}
Controls.Add(p);
}
private ITemplate _template;
[TemplateContainer(typeof(Test.TestTemplate))]
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateInstance(TemplateInstance.Single)]
public virtual ITemplate Template
{
get { return _template; }
set { _template = value; }
}
private class TestTemplate : Control, INamingContainer
{
}
}
Here is the codebehind of the aspx page:
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Button1_Click(object sender, EventArgs e)
{
Label1.Text = Button1.Text + " clicked";
}
And the aspx markup:
<form id="form1" runat="server">
<div>
<cc:Test ID="Test1" runat="server">
<Template>
<asp:Button ID="Button1" runat="server" Text="BUTTON" OnClick="Button1_Click" />
</Template>
</cc:Test>
<cc:Test ID="Test2" runat="server">
<Template>
<asp:Label ID="Label1" runat="server">text</asp:Label>
</Template>
</cc:Test>
</div>
</form>
My reply:
If you add
Context.Response.Write(this.ID + "<br>");
to CreateChildControls and put the code in Button1_Click into try...catch
protected void Button1_Click(object sender, EventArgs e)
{
try
{
Label1.Text = Button1.Text + " clicked";
}
catch { }
}
you'd note that it *does* get called for both controls. So reason is not that the Label wouldn't exist but they are in two separate controls where template container (TestTemplate) is a naming container, which means you cannot access the (sub)control straight with the ID. You would need to use FindControl on Page to access the other control from other and also provide some means for the template container to be accessible via the public API of your Test control. Something like
1. Create a read-only property to your control to access the Template container, and also bit modifying your class structure
public class Test : CompositeControl
{
private TestTemplate f = null;
protected override void CreateChildControls()
{
Controls.Clear();
PlaceHolder p = new PlaceHolder();
if (Template != null)
{
f = new TestTemplate();
f.ID = "TestTemplate1";
Template.InstantiateIn(f);
p.Controls.Add(f);
}
Controls.Add(p);
}
private ITemplate _template;
[TemplateContainer(typeof(TestTemplate))]
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateInstance(TemplateInstance.Single)]
public virtual ITemplate Template
{
get { return _template; }
set { _template = value; }
}
[Browsable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public TestTemplate TestTemplate
{
get
{
EnsureChildControls();
return f;
}
}
}
public class TestTemplate : Control, INamingContainer
{
}
2. Then changing the code in Button1_Click:
protected void Button1_Click(object sender, EventArgs e)
{
((Label)Test2.TestTemplate.FindControl("Label1") ).Text = Button1.Text + " clicked";
}
So essentially accessing Button1 in the methods works since the event handler is sort of executing in the same naming container "context" while Label1 is in other "context" and accessing it (Label) requires bit FindControl'ing.