【问题标题】:Dynamic created controls inside UpdatePanel?在 UpdatePanel 中动态创建控件?
【发布时间】:2010-11-03 10:56:14
【问题描述】:

我想创建允许用户设计自己的网站结构的控件。我想象在UpdatePanel 内部是带有TextBox(用于页面名称)和Button“添加到下方”的控件。它看起来像:

|   "Questions" |
| [ add below ] |

|     "Tags"    |
| [ add below ] |

|    "Badges"   |
| [ add below ] |

现在,当用户单击“标签”元素中的按钮时,应该会在“标签”和“徽章”之间出现新的按钮,其名称可编辑,因此用户可以将其命名为“用户”。应该在没有完整回发的情况下完成(以避免页面闪烁)。

现在是我的问题:我无法在 onInit 中加载这些控件(最后不是全部),因为它们不存在,但我必须处理他们的点击事件,所以我应该附加事件侦听器在初始化阶段应该做什么. 我怎样才能实现所描述的功能?

我玩了太多时间,我很困惑。如有任何提示,我将不胜感激。

【问题讨论】:

    标签: asp.net dynamic-controls


    【解决方案1】:

    这是一个棘手的情况,因为您正在处理动态控件,您需要在页面初始化时填充它们以保持视图状态,而且在按钮单击期间添加到更新面板中的控件的事件似乎直到下一次才会注册回发,因此正常的按钮单击事件仅在您单击新添加的控件时每隔一次触发一次。除了我的做法之外,可能还有其他方法可以解决这个问题。如果有人知道,我想知道。

    我的解决方案是保留一个动态添加的列表,并将其存储在会话变量中(因为在页面初始化期间未加载视图状态)。然后在页面上初始化加载您之前添加的任何控件。然后在页面加载期间使用一些自定义代码或正常的点击事件来处理点击事件。

    我创建了一个示例页面来帮助测试。

    这里是aspx页面的代码(default.aspx):

    <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head id="Head1" runat="server">
        <title></title>
    </head>
    <body>
        <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
        <p>This only updates on full postbacks: <%= DateTime.Now.ToLongTimeString() %></p>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:PlaceHolder ID="PlaceholderControls" runat="server"></asp:PlaceHolder>
            </ContentTemplate>
        </asp:UpdatePanel>
        </form>
    </body>
    </html>
    

    这是页面背后代码的代码(default.aspx.cs):

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    public partial class _Default : System.Web.UI.Page
    {
        /// <summary>
        /// this is a list for storing the id's of dynamic controls
        /// I have to put this in the session because the viewstate is not
        /// loaded in the page init where we need to use it
        /// </summary>
        public List<string> DynamicControls
        {
            get
            {
                return (List<string>)Session["DynamicControls"];
            }
            set
            {
                Session["DynamicControls"] = value;
            }
        }
    
        protected void Page_Init(object sender, EventArgs e)
        {
            PlaceholderControls.Controls.Clear();
    
            //add one button to top that will cause a full postback to test persisting values--
            Button btnFullPostback = new Button();
            btnFullPostback.Text = "Cause Full Postback";
            btnFullPostback.ID = "btnFullPostback";
            PlaceholderControls.Controls.Add(btnFullPostback);
    
            PlaceholderControls.Controls.Add(new LiteralControl("<br />"));
    
            PostBackTrigger FullPostbackTrigger = new PostBackTrigger();
            FullPostbackTrigger.ControlID = btnFullPostback.ID;
    
            UpdatePanel1.Triggers.Add(FullPostbackTrigger);
            //-----------------------------------------------------------------------
    
    
    
    
            if (!IsPostBack)
            {
                //add the very first control           
                DynamicControls = new List<string>();
    
                //the DynamicControls list will persist because it is in the session
                //the viewstate is not loaded yet
                DynamicControls.Add(AddControls(NextControl));
    
            }
            else
            {
                //we have to reload all the previously loaded controls so they
                //will have been added to the page before the viewstate loads
                //so their values will be persisted
                for (int i = 0; i < DynamicControls.Count; i++)
                {
                    AddControls(i);
                }
            }
    
    
        }
    
        protected void Page_Load(object sender, EventArgs e)
        {
    
            if (!IsPostBack)
            {
                //we have to increment the initial
                //control count here here be cause we cannot persit data in the viewstate during 
                //page init
    
                NextControl++;
    
            }
            else
            {
                HandleAddNextClick();           
    
            }
    
        }
    
        /// <summary>
        /// this function looks to see if the control which caused the postback was one of our 
        /// dynamically added buttons, we have to do this because the update panel seems to interefere
        /// with the event handler registration.
        /// </summary>
        private void HandleAddNextClick()
        {
            //did any of our dynamic controls cause the postback if so then handle the event
            if (Request.Form.AllKeys.Any(key => DynamicControls.Contains(key)))
            {
                DynamicControls.Add(AddControls(NextControl));
                NextControl++;
            }
        }
    
    
    
        protected void btnAddNext_Command(object sender, CommandEventArgs e)
        {
            //this is intentionally left blank we are handling the click in the page load
            //because the event for controls added dynamically in the click does
            //not get registered until after a postback, so we have to handle it 
            //manually.  I think this has something to do with the update panel, as it works 
            //when not using an update panel, there may be some other workaround I am not aware of
    
        }
    
        /// <summary>
        /// variable for holding the number of the next control to be added
        /// </summary>
        public int NextControl
        {
            get
            {
                return  ViewState["NextControl"] == null ? 0 : (int)ViewState["NextControl"];
            }
            set
            {
                ViewState["NextControl"] = value;
            }
        }
    
    
        /// <summary>
        /// this function dynamically adds a text box, and a button to the placeholder
        /// it returns the UniqueID of the button, which is later used to find out if the button
        /// triggered a postback
        /// </summary>
        /// <param name="ControlNumber"></param>
        /// <returns></returns>
        private string AddControls(int ControlNumber)
        {
            //add textbox
            TextBox txtValue = new TextBox();
            txtValue.ID = "txtValue" + ControlNumber;
            PlaceholderControls.Controls.Add(txtValue);
    
            //add button
            Button btnAddNext = new Button();
            btnAddNext.Text = "Add Control " + ControlNumber;
            btnAddNext.ID = "btnAddNext" + ControlNumber;
            int NextControl = ControlNumber + 1;
            btnAddNext.CommandArgument = NextControl.ToString();
    
            btnAddNext.Command += new CommandEventHandler(btnAddNext_Command);
            PlaceholderControls.Controls.Add(btnAddNext);
    
            //add a line break
            PlaceholderControls.Controls.Add(new LiteralControl("<br />"));
    
            return btnAddNext.UniqueID;
    
        }    
    }
    

    如果此代码对您有帮助,请告诉我。我尝试在了解正在发生的事情及其工作原理的情况下添加 cmets。

    【讨论】:

    • 我一定会在下班后检查您的解决方案并给您更多反馈。非常感谢您提供的样品,即使我没有要求!
    • 像魅力一样工作 :) 我忘记/不知道您使用的一些功能,所以我的解决方案不完整。非常感谢!你帮了我很多:)
    • 很高兴我能提供帮助,但我希望有一种方法可以在不使用会话的情况下做到这一点,但我不知道在加载视图状态之前有任何其他简单的方法来存储和检索信息。您可能可以使用隐藏字段或 cookie,但这些似乎甚至是带有想法的。也许有一种方法可以手动加载视图状态并在页面初始化中使用它。
    • 这不是真的。在 OnLoad 中动态加载控件可以正常工作 - 只要控件树的形状相同! - 因为事件会“赶上”。我一直从 ViewState 恢复动态控件。我存储控件,记住,而是“知道如何重建控件树所需的信息”。重要的是重建同一棵树。使用 placeholder 控件(在 Init 中加载,可能在标记中)确保控件树的形状相同是一种有用的技术:ViewState 读取 作为树.
    • (当我说占​​位符控件在 Init 中加载时,我并不是说 dynamic children 必须在 Init 中加载:创建/添加 children 可以在页面/占位符 ViewState 可访问时推迟到 Load。当孩子被创建/添加时,他们将经历自己的 自己的初始化/加载过程。额外的 ViewState 不会抛出由于存储它的树状结构,因此关闭了主要的 Init/Load!)
    【解决方案2】:

    这种方式适合我

       protected void Page_Load(object sender, EventArgs e)
        {
            if (Page.IsPostBack)
            {
                Button_Click(null, null);
            }
        }
    

    在按钮调用中

     protected void Button_Click(object sender, EventArgs e)
        {
                Panel1.Controls.Clear();
                this.YourMethod();
            }
        }
    

    在调用方法之前清除控件,否则它将加载两次

    【讨论】:

      【解决方案3】:

      如果你想为你的控件动态事件处理程序,那么你可以试试这个 -

      考虑一个按钮控件,

      Button btnTest = new Button();
      btnTest .Click += new EventHandler(btnTest_Click);
      

      你可以让你的代码在按钮点击事件中,

      void btnTest_Click(object sender, EventArgs e)
      {
          //your code
      }
      

      【讨论】:

        猜你喜欢
        • 2017-07-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-10
        相关资源
        最近更新 更多