我在updatepanel中有一个UserControl('AddressInfoGroup'),它通过loadControl方法动态加载用户控件('AddressInfo')。我在同一页面上有另外两个这样的控件,两者都能正常工作。
当AddressInfoGroup控件向页面呈现AddressInfo控件时,AddressInfo中的文本框的id与ascx标记保持不变。这些id应该由.NET clientID进程动态生成。因此,当第二个AddressInfo控件添加到AddressInfoGroup时,我得到一个运行时异常“具有相同键的条目已存在”。两个工作控件产生适当的clientID,因此不会返回此错误。
我还应该提到AddressInfoGroup控件有几个按钮,这些按钮使用clientID正确呈现,因此它似乎是AddressInfo控件本身的问题。任何建议都会非常有用!
<%@ 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>
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();
}
}
}
<%@ 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>
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;
}
}
}
答案 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();
}