Here is what I want:
I want a control to put on a page, which other developers can place form elements inside of to display the entities that my control is searching. I have the Searching logic all working. The control builds custom search fields and performs searches based on declarative C# classes implementing my SearchSpec interface.
Here is what I've been trying:
I've tried using ITemplate on a WebControl which implements INamingContainer
I've tried implementing a CompositeControl
The closest I can get to working is below.
OK I have a custom WebControl
[
AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal),
AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal),
DefaultProperty("SearchSpecName"),
ParseChildren(true),
ToolboxData("<{0}:SearchPage runat=\"server\"> </{0}:SearchPage>")
]
public class SearchPage : WebControl, INamingContainer
{
[Browsable(false),
PersistenceMode(PersistenceMode.InnerProperty),
DefaultValue(typeof(ITemplate), ""),
Description("Form template"),
TemplateInstance(TemplateInstance.Single),
TemplateContainer(typeof(FormContainer))]
public ITemplate FormTemplate { get; set; }
public class FormContainer : Control, INamingContainer{ }
public Control MyTemplateContainer { get; private set; }
[Bindable(true), Category("Behavior"), DefaultValue(""),
Description("The class name of the SearchSpec to use."), Localizable(false)]
public virtual string SearchSpecName
{
get;
set;
}
[Bindable(true), Category("Behavior"), DefaultValue(true),
Description("True if this is query mode."), Localizable(false)]
public virtual bool QueryMode
{
get;
set;
}
private SearchSpec _spec;
private SearchSpec Spec
{
get
{
if (_spec == null)
{
Type type = Assembly.GetExecutingAssembly().GetTypes().Where(t => t.Name == SearchSpecName).First();
_spec = (SearchSpec)Assembly.GetExecutingAssembly().CreateInstance(type.Namespace + "." + type.Name);
}
return _spec;
}
}
protected override void CreateChildControls()
{
if (FormTemplate != null)
{
MyTemplateContainer = new FormTemplateContainer(this);
FormTemplate.InstantiateIn(MyTemplateContainer);
Controls.Add(MyTemplateContainer);
}
else
{
Controls.Add(new LiteralControl("blah"));
}
}
protected override void RenderContents(HtmlTextWriter writer)
{
// <snip>
}
protected override HtmlTextWriterTag TagKey
{
get
{
return HtmlTextWriterTag.Div;
}
}
}
public class FormTemplateContainer : Control, INamingContainer
{
private SearchPage parent;
public FormTemplateContainer(SearchPage parent)
{
this.parent = parent;
}
}
then the usage:
<tster:SearchPage ID="sp1" runat="server" SearchSpecName="TestSearchSpec" QueryMode="False">
<FormTemplate>
<br />
Test Name:
<asp:TextBox ID="testNameBox" runat="server" Width="432px"></asp:TextBox>
<br />
Owner:
<asp:TextBox ID="ownerBox" runat="server" Width="427px"></asp:TextBox>
<br />
Description:
<asp:TextBox ID="descriptionBox" runat="server" Height="123px" Width="432px"
TextMode="MultiLine" Wrap="true"></asp:TextBox>
</FormTemplate>
</tster:SearchPage>
The problem is that in the CodeBehind, the page has members descriptionBox, ownerBox and testNameBox. However, they are all null. Furthermore, FindControl("ownerBox") returns null as does this.sp1.FindControl("ownerBox"). I have to do this.sp1.MyTemplateContainer.FindControl("ownerBox") to get the control.
How can I make it so that the C# Code Behind will have the controls generated and not null in my Page_Load event so that developers can just do this:
testNameBox.Text = "foo";
ownerBox.Text = "bar";
descriptionBox.Text = "baz";