没有为加载的控件生成ASP.NET客户端ID

时间:2010-03-01 21:16:53

标签: c# .net user-controls

我在updatepanel中有一个UserControl('AddressInfoGroup'),它通过loadControl方法动态加载用户控件('AddressInfo')。我在同一页面上有另外两个这样的控件,两者都能正常工作。

当AddressInfoGroup控件向页面呈现AddressInfo控件时,AddressInfo中的文本框的id与ascx标记保持不变。这些id应该由.NET clientID进程动态生成。因此,当第二个AddressInfo控件添加到AddressInfoGroup时,我得到一个运行时异常“具有相同键的条目已存在”。两个工作控件产生适当的clientID,因此不会返回此错误。

我还应该提到AddressInfoGroup控件有几个按钮,这些按钮使用clientID正确呈现,因此它似乎是AddressInfo控件本身的问题。任何建议都会非常有用!

AddressInfoGroup标记

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AddressInfoGroup.ascx.cs" Inherits="MFRI.Controls.Contact.AddressInfoGroup" %>

<h2>Address information</h2>
<div class="formGroup">
    <asp:PlaceHolder ID="plc_infoCtrls" runat="server" />
    <asp:Button id="btn_addAddress" CssClass="btn_add" Text="v" runat="server" OnClick="addAddress_click" UseSubmitBehavior="false" CausesValidation="false"  />
    <asp:Button id="btn_removeAddress" CssClass="btn_remove" Text="^" runat="server" OnClick="removeAddress_click" UseSubmitBehavior="false" CausesValidation="false" />
</div>

AddressInfoGroup codebehind

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using MFRI.Common.Contact;
using MFRI.Common;

namespace MFRI.Controls.Contact
{
    public partial class AddressInfoGroup : System.Web.UI.UserControl
    {
        private int _maxNumberOfControls = 3;
        private int _minNumberOfControls = 1;
        private List<Address> _controlListToBind = null;

        public int NumberOfControls
        {
            get { return SafeConvert.ToInt(ViewState["NumberOfControls"] ?? 0); }
            set { ViewState["NumberOfControls"] = value; }
        }
        public bool Editable
        {
            get { return (bool)(ViewState["InfoControlsEditable"] ?? false); }
            set { ViewState["InfoControlsEditable"] = value; }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            if (_controlListToBind != null && _controlListToBind.Count > 0)
            {
                NumberOfControls = _controlListToBind.Count;
                foreach (Address address in _controlListToBind)
                {
                    AddressInfo addressInfo = (AddressInfo)LoadControl("AddressInfo.ascx");
                    addressInfo.InitControl(Editable, address);
                    plc_infoCtrls.Controls.Add(addressInfo);
                }
            }
            else
            {
                if (NumberOfControls <= 0)
                    NumberOfControls = 1;
                for (int i = 0; i < NumberOfControls; i++)
                {
                    AddressInfo addressInfo = (AddressInfo)LoadControl("AddressInfo.ascx");
                    addressInfo.InitControl(Editable, null);
                    plc_infoCtrls.Controls.Add(addressInfo);
                }
            }
            RefreshButtons();
        }

        public void BindAddressList(List<Address> addressList)
        {
            _controlListToBind = addressList;
        }

        public List<Address> GetAddressList()
        {
            List<Address> returnList = new List<Address>();
            foreach (AddressInfo addressInfo in plc_infoCtrls.Controls.OfType<AddressInfo>())
            {
                returnList.Add(addressInfo.GetAddress());
            }
            return returnList;
        }

        private void RefreshButtons()
        {
            btn_addAddress.Enabled = false;
            btn_removeAddress.Enabled = false;
            if (Editable)
            {
                if (NumberOfControls > _minNumberOfControls)
                    btn_removeAddress.Enabled = true;
                if (NumberOfControls < _maxNumberOfControls)
                    btn_addAddress.Enabled = true;
            }
        }

        protected void addAddress_click(object sender, EventArgs e)
        {
            if (NumberOfControls < _maxNumberOfControls)
            {
                AddressInfo addressInfo = (AddressInfo)LoadControl("AddressInfo.ascx");
                addressInfo.InitControl(Editable, null);
                plc_infoCtrls.Controls.Add(addressInfo);
                NumberOfControls++;
            }
            RefreshButtons();
        }

        protected void removeAddress_click(object sender, EventArgs e)
        {
            if (_minNumberOfControls < NumberOfControls)
            {
                plc_infoCtrls.Controls.RemoveAt(plc_infoCtrls.Controls.Count - 1);
                NumberOfControls--;
            }
            RefreshButtons();
        }
    }
}

AddressInfo标记

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AddressInfo.ascx.cs" Inherits="MFRI.Controls.Contact.AddressInfo" %>

<div>
    <asp:Label ID="lbl_line1" AssociatedControlID="txt_line1" runat="server">Line 1:</asp:Label><asp:TextBox ID="txt_line1" runat="server"></asp:TextBox>
    <asp:RequiredFieldValidator id="val_line1" runat="server" ErrorMessage="Please include a street address" ControlToValidate="txt_line1" Display="Dynamic">*</asp:RequiredFieldValidator>
</div>
<div>
    <asp:Label ID="lbl_line2" AssociatedControlID="txt_line2" runat="server">Line 2:</asp:Label><asp:TextBox ID="txt_line2" runat="server"></asp:TextBox>
</div>
<div>
    <asp:Label ID="lbl_zip" AssociatedControlID="txt_zip" runat="server">Zip code:</asp:Label><asp:TextBox ID="txt_zip" runat="server"></asp:TextBox>
    <asp:RequiredFieldValidator id="val_zip" runat="server" ErrorMessage="Please include a zip code" ControlToValidate="txt_zip" Display="Dynamic">*</asp:RequiredFieldValidator>
    <asp:RegularExpressionValidator id="regVal_zip" ControlToValidate="txt_zip" ErrorMessage="Zip code must be made up of integers in the format xxxxx" ValidationExpression="^\d{5}$" Runat="server" Display="Dynamic">*</asp:RegularExpressionValidator>
</div>
<div>
    <asp:Label ID="lbl_city" AssociatedControlID="txt_city" runat="server">City:</asp:Label><asp:TextBox ID="txt_city" runat="server" CssClass="disabled"></asp:TextBox>
</div>
<div>
    <asp:Label ID="lbl_state" AssociatedControlID="ddl_state" runat="server">State:</asp:Label><asp:DropDownList ID="ddl_state" runat="server" Enabled="false"></asp:DropDownList>
</div>
<div>
    <asp:Label ID="lbl_edit" AssociatedControlID="chk_edit" runat="server">Edit City/State:</asp:Label><asp:CheckBox ID="chk_edit" runat="server" />
</div>

AddressInfo codebehind

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using MFRI.Common.Contact;
using System.Text;
using System.Globalization;
using System.Threading;
using MFRI.Common;

namespace MFRI.Controls.Contact
{
    public partial class AddressInfo : System.Web.UI.UserControl
    {
        private bool _editable = false;
        private bool _allowStateCityEdit = false;

        protected bool AllowStateCityEdit{
            set { _allowStateCityEdit = value; }
        }

        protected ClientScriptManager clientScriptManager
        {
            get { return Page.ClientScript; }
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            txt_city.Attributes.Add("readonly", "readonly");
            RegisterEditCityStateScript();
            if (_editable)
                RegisterZipCodeScript();
        }

        public void InitControl(bool editable, Address address)
        {
            InitStateDropDown();

            if (!_allowStateCityEdit)
            {
                lbl_edit.Visible = false;
                chk_edit.Visible = false;
            }

            _editable = editable;
            if (!_editable)
            {
                txt_line1.Enabled = false;
                val_line1.Enabled = false;

                txt_line2.Enabled = false;
                txt_city.Enabled = false;

                txt_zip.Enabled = false;
                val_zip.Enabled = false;
                regVal_zip.Enabled = false;

                ddl_state.Enabled = false;
                chk_edit.Enabled = false;
            }
            else
            {
                txt_zip.Attributes.Add("onblur", "GetCityState('" + txt_city.ClientID + "','" + ddl_state.ClientID + "','" + txt_zip.ClientID + "');");
                chk_edit.Attributes.Add("onClick", "ToggleCityState('" + txt_city.ClientID + "','" + ddl_state.ClientID + "','" + txt_zip.ClientID + "')");
            }

            if (address != null)
            {
                txt_line1.Text = address.Line1;
                txt_line2.Text = address.Line2;
                txt_city.Text = address.City;
                txt_zip.Text = SafeConvert.ToString(address.Zip);
                ddl_state.SelectedValue = SafeConvert.ToString((int)address.State);
            }
        }

        private void RegisterZipCodeScript(){
            if (!clientScriptManager.IsClientScriptBlockRegistered(this.GetType(), "zipCodeScript"))
            {
                StringBuilder script = new StringBuilder();
                script.Append("function GetCityState(txtCityId,ddlStateId,txtZipId) {\n");
                script.Append("  var textZip = document.getElementById(txtZipId);\n");
                script.Append("  var zipCode = parseFloat(textZip.value);\n");
                script.Append("  if(isNaN(zipCode)){\n");
                script.Append("    var txtCity = document.getElementById(txtCityId);\n");
                script.Append("    var ddlState = document.getElementById(ddlStateId);\n");
                script.Append("    txtCity.value = '';\n");
                script.Append("    ddlState.selectedIndex = 0;\n");
                script.Append("  }\n");
                script.Append("  else{\n");
                script.Append("    MFRI.Controls.Contact.ZipCodeService.GetCityState(zipCode, txtCityId, ddlStateId, SucceededCallback);\n");
                script.Append("  }\n");
                script.Append("}\n");
                script.Append("function SucceededCallback(result) {\n");
                script.Append("  var txtCity = document.getElementById(result[0]);\n");
                script.Append("  txtCity.value = result[2];\n");
                script.Append("  var ddlState = document.getElementById(result[1]);\n");
                script.Append("  var stateId = result[3];\n");
                script.Append("  for(i=0; i<ddlState.options.length; i++){\n");
                script.Append("    if(ddlState.options[i].value == stateId){\n");
                script.Append("      ddlState.selectedIndex = i;\n");
                script.Append("    }\n");
                script.Append("  }\n");
                script.Append("}\n");
                clientScriptManager.RegisterClientScriptBlock(this.GetType(), "zipCodeScript", script.ToString(), true);
            }
        }

        private void RegisterEditCityStateScript()
        {
            if (!clientScriptManager.IsClientScriptBlockRegistered(this.GetType(), "editCityState"))
            {
                StringBuilder script = new StringBuilder();
                script.Append("function ToggleCityState(txtCityId, ddlStateId, txtZipId) {\n");
                script.Append("  var txtCity = document.getElementById(txtCityId);\n");
                script.Append("  var ddlState = document.getElementById(ddlStateId);\n");
                script.Append("  if(ddlState.disabled == true){\n");
                script.Append("    txtCity.removeAttribute('readonly');\n");
                script.Append("    ddlState.disabled = false;\n");
                script.Append("  }\n");
                script.Append("  else{\n");
                script.Append("    txtCity.setAttribute('readonly', 'readonly');\n");
                script.Append("    ddlState.disabled = true;\n");
                script.Append("    GetCityState(txtCityId,ddlStateId,txtZipId); \n");
                script.Append("  }\n");
                script.Append("}\n");
                clientScriptManager.RegisterClientScriptBlock(this.GetType(), "editCityState", script.ToString(), true);
            }
        }

        private void InitStateDropDown(){
            CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture;
            TextInfo textInfo = cultureInfo.TextInfo;

            ddl_state.Items.Add(new ListItem("Select a state","-1"));
            foreach (StateType state in Enum.GetValues(typeof(StateType))) 
            {
                string displayName = state.ToString().ToLower();
                displayName = displayName.Replace('_', ' ');
                ddl_state.Items.Add(new ListItem(textInfo.ToTitleCase(displayName), state.GetHashCode().ToString()));
            }
        }

        public Address GetAddress()
        {
            Address address = new Address();
            address.Line1 = txt_line1.Text;
            address.Line2 = txt_line2.Text;
            address.State = (StateType)Enum.ToObject(typeof(StateType), SafeConvert.ToInt(ddl_state.SelectedValue));
            address.City = txt_city.Text;
            address.Zip = SafeConvert.ToInt(txt_zip.Text);
            return address;
        }
    }
}

3 个答案:

答案 0 :(得分:3)

我终于明白了。

每个addressInfo控件都是由addressInfoGroup即时创建的。实例化后,addressInfoGroup调用新创建的addressInfo控件的'initControl'方法。在该init函数中,我检索了一些clientID值:

txt_zip.Attributes.Add("onblur", "GetCityState('" + txt_city.ClientID + "','" + ddl_state.ClientID + "','" + txt_zip.ClientID + "');");
chk_edit.Attributes.Add("onClick", "ToggleCityState('" + txt_city.ClientID + "','" + ddl_state.ClientID + "','" + txt_zip.ClientID + "')");

由于我对clientID的引用,addressInfo中的所有控件都保留了标记ID。如果我评论这些行,.NET会生成正确的Id。这与调用这些行时有关。当我将这些行移动到addressInfo的回发中时,一切都按预期工作。

最终结果:如果父控件告诉子控件引用clientID,则所有子控件clientID似乎都会出现故障。

答案 1 :(得分:0)

您是否考虑过使用ListView控件动态创建和显示您的地址控件?您将在页面上对控件的一个实例进行硬编码,然后在ListView的ItemTemplate中放入Address控件并将其数据绑定到ListView的DataSource属性中的数据。

答案 2 :(得分:0)

您是否尝试过显式设置动态添加的AddressInfo控件的ID值?例如,在AddressInfoGroup的Page_Load中:

protected void Page_Load(object sender, EventArgs e)
{
    if (_controlListToBind != null && _controlListToBind.Count > 0)
    {
        NumberOfControls = _controlListToBind.Count;
        int index = 0;
        foreach (Address address in _controlListToBind)
        {
            AddressInfo addressInfo = (AddressInfo)LoadControl("AddressInfo.ascx");
            addressInfo.ID = String.Format("AddressInfo{0}", index++);
            addressInfo.InitControl(Editable, address);
            plc_infoCtrls.Controls.Add(addressInfo);
        }
    }
    else
    {
        if (NumberOfControls <= 0)
            NumberOfControls = 1;
        for (int i = 0; i < NumberOfControls; i++)
        {
            AddressInfo addressInfo = (AddressInfo)LoadControl("AddressInfo.ascx");
            addressInfo.ID = String.Format("AddressInfo{0}", i);
            addressInfo.InitControl(Editable, null);
            plc_infoCtrls.Controls.Add(addressInfo);
        }
    }
    RefreshButtons();
}