Ошибка: SelectedValue, который является недопустимым, потому что он не существует в списке элементов

У меня есть Gridview, который привязывается к ObjectDataSource (objStudentDetails). В режиме редактирования / вставки Gridview одно из полей представляет собой DropDownList, который получает параметры списка выбора из справочной таблицы. У меня есть эта привязка DropDownList к другому элементу управления ObjectDataSource (objStateList), который представляет таблицу поиска. Он работает нормально, пока значение в objStudentDetails ObjectDataSource совпадает с одним из значений в objStateList ObjectDataSource, по крайней мере, в случае непустого строкового значения в любом случае.

У objStateList есть эти значения (из хранимого процесса, который его загружает - ID#6 - пустая строка ''):

StateId     State
----------- -----
6             
4           AL
1           GA
3           KY
2           TN

Объект objStudentDetails имеет следующие значения (из хранимого процесса, который его загружает):

FirstName   LastName   State
----------- ---------- -----
tone        smith      TN

Или он может иметь такой набор результатов (State - пустая строка - ''):

FirstName   LastName   State
----------- ---------- -----
jenny       johnson     

В первом наборе результатов objStudentDetails состояние DropDownList в EditItemTemplate отображается нормально. Однако во втором наборе результатов я получаю эту ошибку:

'ddlEditState' has a SelectedValue which is invalid because it does not exist in the list of items.
Parameter name: value 

Я думаю, что, поскольку моя таблица поиска имеет значение с пустой строкой, значение objStudentDetails с пустой строкой для состояния будет соответствовать, но что-то не работает так, как я ожидаю.

Вот мой код EditItemTemplate из Gridview:

<EditItemTemplate>
  <asp:Panel ID="panEditState" runat="server">
    <asp:DropDownList ID="ddlEditState" runat="server" CssClass="GridviewDropdownlist"
      DataSourceID="objStateList" DataTextField="State" DataValueField="State"      
      SelectedValue='<%# Bind("State") %>'
      Width="50px">
</asp:DropDownList>
</asp:Panel>
</EditItemTemplate>

И objStateList, который вызывает метод, передающий параметр таблицы поиска для запроса:

<asp:ObjectDataSource ID="objStateList" runat="server" SelectMethod="GetDropdownData"     TypeName="AIMLibrary.BLL.DropdownData">
<SelectParameters>
<asp:Parameter Name="itemsToGet" DefaultValue="state" />
</SelectParameters>
</asp:ObjectDataSource>

Есть идеи?

4 ответа

Решение

Начните с того, что установите свойство AppendDataBoundItems обоих DropDownLists в значение true. Затем добавьте NULL ListItem, добавив следующее <asp:ListItem> элемент для каждого DropDownList, так что декларативная разметка выглядит следующим образом:

<asp:DropDownList ID="Categories" runat="server"
    DataSourceID="CategoriesDataSource" DataTextField="CategoryName"
    DataValueField="CategoryID" SelectedValue='<%# Bind("CategoryID") %>'
    AppendDataBoundItems="True">
    <asp:ListItem Value="">[nothing selected]</asp:ListItem>
</asp:DropDownList>

Я подозреваю, что есть много разных сценариев, которые могут вызвать эту ошибку. В моем случае у меня был раскрывающийся список в поле шаблона. Раскрывающийся список был связан с собственным источником данных объекта, а его свойство selectedvalue было связано с полем из собственного (отдельного) источника данных gridview.

Теперь, с моим конкретным сценарием, проблема была в состоянии гонки. Источник данных gridview заполнялся и связывался ДО того, как выпадающие списки имели свою очередь. Это также означало, что выбранные значения раскрывающихся списков устанавливались ДО того, как элементы раскрывающихся списков были созданы с помощью их собственных привязок.

Я уверен, что должно быть лучшее решение, но у меня не было много времени для исследований. Я отключил сетку и выпадающие списки от их источников данных (то есть, удалив назначения из конструктора) и выбрал привязку программно. Таким образом, я могу явно связать выпадающие списки так, чтобы значения их элементов были доступны, когда само представление сетки привязано.

Все идет нормально. Всего несколько дополнительных строк кода в Page_Load

AppendDataBoundItems="True"> работает, но не во всех случаях. Создание выпадающего списка внутри GridView все еще остается загадкой, которую Microsoft должна решить. Говорят, что разработка в ASP намного быстрее, чем в PHP. Ну, это мой третий день на эту маленькую проблему и до сих пор не имеют решения.

Хорошо, так как это распространенная проблема, я думаю, стоит опубликовать ответ: после долгих осмотров я нашел два решения - ну, один патч и один реальный.

  1. Patching: установите настройку DDL AppendDataBoundItem=true и добавить вручную один элемент в список (например, "Пожалуйста, выберите" с нулевым значением):

    Пожалуйста, выберите

Кажется, это работает примерно в 80% случаев. У меня была странная ситуация, когда мне приходилось обновлять существующий (и рабочий) запрос, используемый DDL, чтобы разрешить другое значение параметра - Query был чем-то похожим на SELECT ID, Name from EMPLOYEES where Department =@Department и изначально @Department мог быть равен только "Планировщикам" и "Мастерской" - после добавления "Логистика" DDL загадочно перестал работать ТОЛЬКО для нового значения отдела.

  1. Правильное решение: связать DDL во время GridView_RowDataBound событие (спасибо за эту статью

Мой параметр берется как текст с метки (настраивается где-то еще)

    protected void GridView5_RowDataBound(object sender, GridViewRowEventArgs e)
    {

    //********** this  is a workaround for the annoying problem with dropdownlist in gidview without adding new item ************
    if (e.Row.RowType == DataControlRowType.DataRow && GridView5.EditIndex == e.Row.RowIndex)
    {
        DropDownList DropDownList5 = (DropDownList)e.Row.FindControl("DropDownList5");
        string query = "SELECT gkey as empID, name FROM [employees] where department=@department";
        SqlCommand command = new SqlCommand(query);
        command.Parameters.AddWithValue("@department", lblDepartment.Text);
        DropDownList5.DataSource = GetData(command);
        DropDownList5.DataTextField = "name";
        DropDownList5.DataValueField = "empID";
        DropDownList5.DataBind();
    }

И метод GetData:

   private DataTable GetData (SqlCommand cmd)
{
    string strConnString = ConfigurationManager.ConnectionStrings["MyConnectionString"].ConnectionString;
    using (SqlConnection con = new SqlConnection(strConnString))
    {
        using (SqlDataAdapter sda = new SqlDataAdapter())
        {
            cmd.Connection = con;
            sda.SelectCommand = cmd;
            using (DataTable dt= new DataTable())
            {
                sda.Fill(dt);
                return dt;
            }
        }
    }
}
Другие вопросы по тегам