【问题标题】:OnChange Events for Dynamically rendered User Controls in ASP.NETASP.NET 中动态呈现的用户控件的 OnChange 事件
【发布时间】:2015-07-06 23:02:16
【问题描述】:

我在页面 (ABC.aspx) 中以 5 到 6 个选项卡显示了一组用户控件。每个用户控件都有许多文本框、下拉菜单等。我需要检测这些用户控件中任何字段的任何用户更改,并在这些用户控件所在的父页面 (ABC.aspx) 上进行一些处理。

用户控件的实现如下。 每个用户控件都有一个 XMl 文件,该文件被读取并相应地创建控件。

ascx.cs 中的 render 方法读取此 xml 文件并相应地为该用户控件呈现 UI。

假设用户控件的示例 XMl 是

 <uigroup groupname ="Schematic(Logical) Symbol Request">
<uirow>
  <uicontrol displayname="Request Type" datatype="DropDown" isrequired="false" domainname="RequestType" dropdownEventHandler="OnScenarioChange(this)" key="Schematic Scenario" defaultValue="New"></uicontrol>
</uirow>
<uirow>
  <uicontrol displayname="Logical Name" datatype="TextBox" isrequired="false" supportedscenarios="New,Use Existing,Update Existing" key="Schematic Symbol Name"></uicontrol>
  <uicontrol displayname="Similar Symbol" datatype="TextBox" isrequired="false" supportedscenarios="New" key="Similar Schematic Symbol"></uicontrol>
</uirow>
<uirow>
  <uicontrol displayname="Type of Change" datatype="DropDown" isrequired="false" domainname="Type of Schematic Change" supportedscenarios="Update Existing" key="Type of Schematic Change"></uicontrol>
  <uicontrol displayname="Layout Preference" datatype="DropDown" isrequired="false" domainname="Schematic Layout Preference" supportedscenarios="New,Update Existing" key="Schematic Layout Preference"></uicontrol>
</uirow>
<uirow>
  <uicontrol displayname="Justification" datatype="DropDown" isrequired="false" domainname="Schematic Justification" supportedscenarios="Update Existing" key="Schematic Justification"></uicontrol>
  <uicontrol displayname="Logical Directory" datatype="DropDown" isrequired="false"   domainname="ICL Logical Directory" supportedscenarios="New" key="ICL Logical Directory"></uicontrol>
</uirow>

Usercontrol.ascx.cs 有

protected void Page_Load(object sender, EventArgs e)
    {
    }


    protected void Page_Init(object sender, EventArgs e)
    {
        this.Render();
    }

    public void Render()
    {
        this.Render(this.XmlPath);
    }

我的渲染函数做了以下事情

public void Render(string _filePath)
    {


        UITab tab = this.GetDataFromXML(_filePath);
        foreach (UIGroup g in tab.Groups)
        {
            //Add a panel
            Panel groupPanel = this.AddPanel(g);
            foreach (UIRow r in g.Rows)
            {
                Table table = new Table();
                groupPanel.Controls.Add(table);

                TableRow tableRow = new TableRow();
                table.Rows.Add(tableRow);

                foreach (UIControl c in r.Controls)
                {
                    switch (c.DataType)
                    {
                        case UIDataType.Textbox:
                            this.AddTextBox(groupPanel, c, tableRow);
                            break;
                        case UIDataType.Dropdown:
                            this.AddDropdown(groupPanel, c, tableRow);
                            break;
                        case UIDataType.LabelInfo:
                            this.AddLabelInfo(groupPanel, c,tableRow);
                            break;
                        case UIDataType.Label:
                            this.AddLabel(groupPanel, c,tableRow);
                            break;
                    }
                }
            }
        }
    }

我们正在以类似的方式呈现其他用户控件。我们需要找到一种方法来检测任何这些用户控件上的用户更改(如文本框更改或下拉列表更改等)并处理父页面。

我不确定 Delegates 和 Events 是否适合,如果可以,它们如何适合这种架构是我最大的问题。任何建设性的意见都会有帮助。

谢谢,

【问题讨论】:

    标签: c# asp.net


    【解决方案1】:

    是的,为您的 UserControl 创建自定义事件很有意义。

    这是一个示例(注意:该示例显示了基本思想,并且不使用 XML 文档作为数据源;尽管如此,该示例应该具有足够的指导性,您可以将其应用于您所构建的内容):

    动态注入嵌套控件的简单用户控件的代码:

    ...在 ascx 文件中:

    <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="TabControl.ascx.cs" Inherits="StackOverflowAspNetQuestionAnswers.Controls.TabControl" %>
    <asp:Panel ID="panel" runat="server">
    </asp:Panel>
    

    ...在用户控件的代码隐藏文件中:

    public partial class TabControl : System.Web.UI.UserControl
        {
            public event EventHandler<ControlChangedEventArgs> ControlUpdated;
    
            protected void Page_Load(object sender, EventArgs e)
            {
                TextBox textBox = new TextBox();
                textBox.AutoPostBack = true;
                textBox.ID = "textBox";
    
                textBox.TextChanged += textBox_TextChanged;
    
                DropDownList dropDown = new DropDownList();
                dropDown.Items.Add(new ListItem("Option 1", "Option 1"));
                dropDown.Items.Add(new ListItem("Option 2", "Option 2"));
                dropDown.AutoPostBack = true;
    
                dropDown.TextChanged += dropDown_TextChanged;
    
                panel.Controls.Add(textBox);
                panel.Controls.Add(dropDown);
            }
    
            void dropDown_TextChanged(object sender, EventArgs e)
            {
                ControlChangedEventArgs args = new ControlChangedEventArgs();
    
                args.ControlID = ((DropDownList)sender).ID;
                args.ControlValue = ((DropDownList)sender).SelectedValue;
    
                ControlUpdated(this, args);
    
                //CODE EDIT:
                UnhookEventHandlers();
    
            }
    
            void textBox_TextChanged(object sender, EventArgs e)
            {
                ControlChangedEventArgs args = new ControlChangedEventArgs();
                args.ControlID = ((TextBox)sender).ID;
                args.ControlValue = ((TextBox)sender).Text;
                ControlUpdated(this, args);
    
                //CODE EDIT:
                UnhookEventHandlers();
    
            }
    
            public virtual void OnControlUpdated(ControlChangedEventArgs e)
            {
                EventHandler<ControlChangedEventArgs> handler = ControlUpdated;
                if (handler != null)
                {
                    handler(this, e);
                }
    
                //CODE EDIT:
                UnhookEventHandlers();
    
            }
    
            //CODE EDIT:
            private void UnhookEventHandlers()
            {
                foreach (var c in panel.Controls.OfType<DropDownList>())
                {
                    c.TextChanged -= dropDown_TextChanged;
                }
    
                foreach (var c in panel.Controls.OfType<TextBox>())
                {
                    c.TextChanged -= textBox_TextChanged;
                }
            }
    
        }
    
        public class ControlChangedEventArgs : EventArgs
        {
            public string ControlID { get; set; }
            public string ControlValue { get; set; }  
        }
    

    下面是使用这个简单选项卡控件的父页面的代码:

    ...在 .aspx 文件中:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="RaisingEventFromUserControl_Question.aspx.cs" Inherits="StackOverflowAspNetQuestionAnswers.RaisingEventFromUserControl_Question" %>
    
    <%@ Register Src="~/Controls/TabControl.ascx" TagPrefix="uc1" TagName="TabControl" %>
    
    
        <!DOCTYPE html>
    
        <html xmlns="http://www.w3.org/1999/xhtml">
        <head runat="server">
            <title></title>
        </head>
        <body>
            <form id="form1" runat="server">
            <div>
                <asp:Label ID="message" runat="server" />
                <uc1:TabControl runat="server" id="TabControl" />
            </div>
            </form>
        </body>
        </html>
    

    ...在父页面的代码隐藏文件中:

     public partial class RaisingEventFromUserControl_Question : System.Web.UI.Page
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                TabControl.ControlUpdated += TabControl_ControlUpdated;
            }
    
            void TabControl_ControlUpdated(object sender, ControlChangedEventArgs e)
            {
               //EDIT: add code to update some data source to lock the form from other users
                message.Text = String.Format("A child control with an ID of '{0}' was updated. It now has the value of '{1}'.", e.ControlID, e.ControlValue);
            }
        }
    

    正如您在示例中看到的,我有一个名为“ControlUpdated”的自定义事件,只要嵌套的 TextBox 或 DropDownList 在运行时动态注入到 UserControl 中,我就会触发该事件。另外,请记住,我为 TextBox 和 DropDownList 设置了“AutoPostBack”属性,以确保在我更改文本框/下拉列表的值时执行由 ASP.Net 生成的客户端代码。

    此外,您可以看到,对于我的自定义事件,我还创建了一个自定义 EventArgs 类,以便我可以传递来自更新的嵌套控件的相关信息(即 ID 和更改的值)。

    【讨论】:

    • 由于我们需要添加到用户控件的每个文本框和下拉菜单都有相同的方法,因此此事件 Onchanged 将添加到用户控件上的所有文本框和下拉菜单中。因此,每当那里发生变化时,都会引发事件,这有点矫枉过正。我需要通知父页面一次并说第一次做一些处理,我知道我们可以使用静态标志来做到这一点,但是从 Javascript 调用 Ajax 方法怎么样?即使这可能是一个潜在的解决方案,我将在下一条评论中发布示例代码。请分享您对两者的看法并提出建议。
    • //On Client Side &lt;script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js" charset="utf-8"&gt; &lt;/script&gt; &lt;script&gt; var locked = false; $("input").change(function() { if(!locked){ $.ajax( { type: "POST", url: "WebForm1.aspx/methodInServer", contentType: "application/json; charset=utf-8", success: function (msg) { locked = true; alert(msg); } }); } }); &lt;/script&gt;
    • //On Server Side [WebMethod] public static string methodInServer() { //Move the code to Process the page here return "Page is locked for other users" }
    • 感谢您对您的要求提供更多说明。我已经编辑了我的答案以解决这个问题。也就是说,我提供服务器端代码解决方案的原因是因为您的问题和示例代码让我相信您需要更多的服务器端解决方案而不是客户端解决方案。
    • 如果您正在寻找客户端解决方案,我建议您不要使用服务器端代码进行 UI 构建(在您的用户控件中),而是转向客户端的 UI 构建。有许多有用的 JavaScript 库(或现有库的插件,例如 jQuery)可以为您提供此功能。最后,我不建议您在事件处理程序中使用“锁定”标志。相反,如果您在第一次更改事件触发后锁定页面,则应该取消事件处理程序的挂钩,以便它们不必继续执行......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-04
    • 1970-01-01
    • 2019-03-20
    • 1970-01-01
    • 2012-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多