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

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

Когда элемент управления AddressInfoGroup отображает на странице элемент управления AddressInfo, текстовые поля в AddressInfo имеют идентификатор, который не отличается от разметки ascx. Эти идентификаторы должны были динамически генерироваться процессом .ID 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 код позади

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 ]

3 голосов
/ 17 марта 2010

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

Каждый элемент управления 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, все заработало как положено.

Итак, конечный результат: если родительский элемент управления сообщает дочернему элементу управления ссылку на clientID, все дочерние элементы управления clientID кажутся неисправными.

0 голосов
/ 05 марта 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();
}
0 голосов
/ 02 марта 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...