Идентификатор клиента ASP.NET не создается для загруженного элемента управления

У меня есть UserControl («AddressInfoGroup») в панели обновлений, которая динамически загружает пользовательские элементы управления («AddressInfo») с помощью метода loadControl. У меня есть два других таких элемента управления на той же странице, оба из которых работают правильно.

Когда элемент управления AddressInfoGroup отображает элемент управления AddressInfo на странице, текстовые поля в AddressInfo имеют идентификаторы, не измененные по сравнению с разметкой ascx. Эти идентификаторы должны были быть динамически сгенерированы процессом .NET clientID. Из-за этого, когда второй элемент управления AddressInfo добавляется в AddressInfoGroup, я получаю исключение времени выполнения «Запись с таким же ключом уже существует». Два рабочих элемента управления создают правильный идентификатор клиента и, следовательно, не возвращают эту ошибку.

Я также должен упомянуть, что элемент управления AddressInfoGroup имеет несколько кнопок, которые правильно отображаются с идентификатором клиента, поэтому, похоже, это проблема с самим элементом управления 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

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;
        }
    }
}

person caltrop    schedule 01.03.2010    source источник
comment
Я не вижу текстовых полей, о которых спрашивает ваш вопрос, нам нужно увидеть разметку элемента управления AddressInfo.   -  person Nick Craver    schedule 02.03.2010
comment
Я включил разметку AddressInfo и программный код, надеюсь, это прольет свет на это =).   -  person caltrop    schedule 02.03.2010


Ответы (3)


Я понял это наконец.

Каждый элемент управления addressInfo создается на лету с помощью addressInfoGroup. После создания экземпляра addressInfoGroup вызывает метод initControl только что созданного элемента управления addressInfo. В этой функции инициализации я получаю несколько значений 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 сохраняют идентификатор разметки. Если я закомментирую эти строки, .NET сгенерирует правильный идентификатор. Это как-то связано с тем, КОГДА вызываются эти строки. Когда я переместил строки в постбэк addressInfo, все заработало, как и ожидалось.

Таким образом, конечный результат: если родительский элемент управления указывает дочернему элементу управления ссылаться на идентификатор клиента, все идентификаторы клиента дочерних элементов управления, похоже, работают со сбоями.

person caltrop    schedule 17.03.2010

Думали ли вы об использовании элемента управления ListView для динамического создания и отображения элементов управления Address на лету? У вас будет один экземпляр элемента управления, жестко закодированный на странице, а затем в ItemTemplate вашего ListView вы поместите свой элемент управления Address и привяжете его к данным в свойстве DataSource вашего ListView.

person Payton Byrd    schedule 02.03.2010
comment
ListView можно было бы использовать вместо решения, которое я создал, но на самом деле это не решает проблему, с которой я столкнулся. Мои группы управления пользователями работают правильно, одна из них просто не создает идентификатор клиента. - person caltrop; 03.03.2010

Вы пытались явно установить значение идентификатора динамически добавляемых элементов управления AddressInfo? Например, в Page_Load группы AddressInfoGroup:

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();
}
person 300 baud    schedule 04.03.2010