我回答了一个相关问题:
https://stackoverflow.com/a/11700540/1268570
但在这个答案中,我会更深入。
我将发布一个带有设计支持和自定义行为的模板化服务器控件:
容器代码
[ToolboxItem(false)]
public class TemplatedServerAddressContainer : WebControl, INamingContainer
{
public string Address { get; protected set; }
public TemplatedServerAddressContainer(string address)
{
this.Address = address;
}
}
- 上述控件将负责保存您想要作为输出发送到控件的数据。这将是您将在其中实例化模板的控件
服务器控制
[DefaultProperty("Address")]
[ToolboxItem(true)]
[ToolboxData("<{0}:TemplatedServerAddressControl runate=server></{0}:TemplatedServerAddressControl>")]
[Designer(typeof(TemplatedServerAddressDesigner))]
//[ToolboxBitmap(typeof(TemplatedServerAddressControl), "")]
[Description("My templated server control")]
[ParseChildren(true)]
public class TemplatedServerAddressControl : WebControl
{
private TemplatedServerAddressContainer addressContainer;
[Bindable(true)]
[Localizable(true)]
[DefaultValue(null)]
[Description("The custom address")]
[Category("Apperance")]
[Browsable(true)]
public string Address
{
get
{
return (this.ViewState["Address"] ?? string.Empty).ToString();
}
set
{
this.ViewState["Address"] = value;
}
}
[Browsable(false)]
[DefaultValue(null)]
[Description("Address template")]
[PersistenceMode(PersistenceMode.InnerProperty)]
[TemplateContainer(typeof(TemplatedServerAddressContainer))]
[TemplateInstance(TemplateInstance.Multiple)]
public ITemplate AddressTemplate { get; set; }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public TemplatedServerAddressContainer AddressContainer
{
get
{
this.EnsureChildControls();
return this.addressContainer;
}
internal set
{
this.addressContainer = value;
}
}
public override ControlCollection Controls
{
get
{
this.EnsureChildControls();
return base.Controls;
}
}
public override void DataBind()
{
this.CreateChildControls();
this.ChildControlsCreated = true;
base.DataBind();
}
protected override void CreateChildControls()
{
this.Controls.Clear();
if (this.AddressTemplate != null)
{
this.addressContainer = new TemplatedServerAddressContainer(this.Address);
this.AddressTemplate.InstantiateIn(this.addressContainer);
this.Controls.Add(this.addressContainer);
}
}
protected override bool OnBubbleEvent(object source, EventArgs args)
{
if (args is CommandEventArgs)
{
var commandArgs = args as CommandEventArgs;
switch (commandArgs.CommandName)
{
case "DoSomething":
// place here your custom logic
this.Page.Response.Write("Command bubbled");
return true;
}
}
return base.OnBubbleEvent(source, args);
}
}
public string Address 属性用作控制输入,您可以创建执行任务所需的所有输入属性。
public ITemplate AddressTemplate { get; set; } 这表示您的控件的模板。您为此属性指定的名称将是页面标记中用作模板名称的名称
public TemplatedServerAddressContainer AddressContainer此属性仅供设计师支持
为了正确创建子控件,您需要覆盖以下方法和属性:Controls、DataBind 和 CreateChildControls
覆盖OnBubbleEvent,您将能够对来自控件的特定事件做出反应。
设计师支持
public class TemplatedServerAddressDesigner : ControlDesigner
{
private TemplatedServerAddressControl controlInstance;
public override void Initialize(IComponent component)
{
this.controlInstance = (TemplatedServerAddressControl)component;
base.Initialize(component);
}
public override string GetDesignTimeHtml()
{
var sw = new StringWriter();
var htmlWriter = new HtmlTextWriter(sw);
var controlTemplate = this.controlInstance.AddressTemplate;
if (controlTemplate != null)
{
this.controlInstance.AddressContainer = new TemplatedServerAddressContainer(
this.controlInstance.Address
);
controlTemplate.InstantiateIn(this.controlInstance.AddressContainer);
this.controlInstance.DataBind();
this.controlInstance.RenderControl(htmlWriter);
}
return sw.ToString();
}
}
ASPX 标记
<%@ Register Assembly="Msts" Namespace="Msts.Topics.Chapter07___Server_Controls.Lesson02___Server_Controls" TagPrefix="address" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
<address:TemplatedServerAddressControl runat="server" ID="addressControl1">
<AddressTemplate>
<b>
Address:
</b>
<u>
<asp:Literal Text="<%# Container.Address %>" runat="server" />
</u>
<asp:Button Text="text" runat="server" OnClick="Unnamed_Click" ID="myButton" />
<br />
<asp:Button Text="Command bubbled" runat="server" CommandName="DoSomething" OnClick="Unnamed2_Click1" />
</AddressTemplate>
</address:TemplatedServerAddressControl>
</asp:Content>
ASPX 代码后面
public partial class TemplatedServerAddress : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
this.addressControl1.Address = "Super Cool";
this.DataBind();
}
protected void Unnamed_Click(object sender, EventArgs e)
{
this.Response.Write("From custom button" + DateTime.Now.ToString());
}
protected void Unnamed2_Click1(object sender, EventArgs e)
{
this.Response.Write("From command button " + DateTime.Now.ToString());
}
}
注意如何在正确的事件中设置控件的属性而不会出现问题:this.addressControl1.Address = "Super Cool";
注意您的控件如何处理自定义事件this.Response.Write("From custom button" + DateTime.Now.ToString());
最后,为了向您的控件指示您想要执行某事,只需创建一个按钮,其命令名称由您的控件公开,如下所示:<asp:Button Text="Command bubbled" runat="server" CommandName="DoSomething" OnClick="Unnamed2_Click1" /> 可选,您的按钮可以包含一个事件处理程序,该处理程序将在事件冒泡之前进行处理。
I uploaded this code sample completely functional to my GitHub for reference