【问题标题】:How to create a templated composite control with a behavior如何创建具有行为的模板化复合控件
【发布时间】:2012-10-03 02:33:30
【问题描述】:

我正在尝试创建一个模板化复合控件,它的工作方式与 ASP.Net 的“PasswordRecovery”控件类似。

也就是说,我的意思是用户可以定义自己的模板,但是通过使用预定义的控件 ID,它定义了哪个字段,比如电子邮件地址,以及哪个按钮是发送电子邮件的按钮邮件。

我尝试查看模板化 Web 服务器控件的文档,但找不到任何关于向这些控件添加行为的内容。

或者,有没有办法完全改变 PasswordRecovery 的行为?我想发送一封电子邮件,其中包含一次性 URL 来更改密码,而不是该控件的常见行为。

【问题讨论】:

    标签: asp.net controltemplate composite-controls


    【解决方案1】:

    我回答了一个相关问题:

    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此属性仅供设计师支持

    • 为了正确创建子控件,您需要覆盖以下方法和属性:ControlsDataBindCreateChildControls

    • 覆盖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());

    • 最后,为了向您的控件指示您想要执行某事,只需创建一个按钮,其命令名称由您的控件公开,如下所示:&lt;asp:Button Text="Command bubbled" runat="server" CommandName="DoSomething" OnClick="Unnamed2_Click1" /&gt; 可选,您的按钮可以包含一个事件处理程序,该处理程序将在事件冒泡之前进行处理。

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

    【讨论】:

    • 哇,这个答案真棒,谢谢!我唯一仍然怀念的是你如何真正取回数据。例如,如果我希望用户可以添加具有特定 ID 的文本框,并在事件处理程序中获取此文本框中包含的信息。
    • 您可以通过在控制级别公开公共属性来实现这一点。 public string Address 属性用作控制输入,您可以创建执行任务所需的所有输入属性。然后像设置任何其他控件属性一样在页面上设置这些属性
    • 不,我的意思是,例如,你有一个文本框,而不是像你的例子中那样只有一个文字。然后,您希望用户在该文本框中添加一些信息,并且您希望该事件检查文本框的值。既然是模板化控件,那么如何获取那个文本框的值呢?
    • 您可以执行以下操作:var t = this.addressContainer as TemplatedServerAddressContainer; var tt = t.FindControl("txt") as TextBox; tt.Text = "jdiajdia"; 在实例化模板并将其添加到页面之后的CreateChildControls 方法中。但是,这不是推荐的方法。如果要向控件传递数据,则应公开公共控件属性
    【解决方案2】:

    模板化用户控件(与模板化服务器控件相对)怎么样。

    这里有一个关于模板化用户控件的 MSDN 教程:http://msdn.microsoft.com/en-us/library/36574bf6(v=vs.100).aspx

    如果您想要预定义的 ID,那么您可以使用 FindControl() 方法从模板中提取控件,然后附加任何您需要的点击事件等。

    例如

    protected void Page_Init(object sender, EventArgs e)
    {
      // if a message template has been defined, check for the reset button
      if(MessageTemplate != null)
      {
        // attempt to grab the reset password button
        Button btnResetPassword = MessageTemplate.FindControl("btnResetPassword ") as Button;
    
        // if the reset password button has been declared, attach the click event             
        if(btnResetPassword != null)
          btnResetPassword .Click += btnResetPassword_Click;  // attach click event
      }
    }
    
    protected void btnResetPassword_Click(object sender, EventArgs e)
    {
      // reset password behaviour here
    }
    

    上面的代码还没有完成/测试,只是为了说明我的意思。不知道是不是你的意思?

    【讨论】:

    • 我不能真正使用用户控件,因为我想将它添加到一个单独的库中,并且用户控件必须在同一个库中才能工作。但实际上,这个想法应该也适用于服务器控件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-14
    • 2013-09-16
    • 1970-01-01
    • 1970-01-01
    • 2011-12-13
    • 2013-01-29
    相关资源
    最近更新 更多