asp.netcontroltemplatecomposite-controls

How to create a templated composite control with a behavior


I'm trying to create a templated composite control that would work in a similar fashion as the "PasswordRecovery" control of ASP.Net.

By that, I mean that the user can define its own template but, by using pre-defined controls ID, it defines which field is, say the e-mail address, and which button is the one to send the e-mail.

I've tried to look at the documentation for templated web server controls, but I can't find anything talking about adding a behavior to those controls.

Alternatively, is there a way to change the behavior of the PasswordRecovery completely? I would like to send an e-mail with a one-time URL to change the password instead of the common behavior of that control.


Solution

  • I answered a related question:

    https://stackoverflow.com/a/11700540/1268570

    But in this answer I will go deeper.

    I will post a templated server control with design support and with custom behavior:

    Container code

    [ToolboxItem(false)]
    public class TemplatedServerAddressContainer : WebControl, INamingContainer
    {
        public string Address { get; protected set; }
    
        public TemplatedServerAddressContainer(string address)
        {
            this.Address = address;
        }
    }
    

    Server Control

    [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);
        }
    }
    

    Designer support

    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 markup

    <%@ 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 code behind

    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());
        }
    }
    

    I uploaded this code sample completely functional to my GitHub for reference