Toshi
Toshi

Reputation: 2608

Prevent Binding of not existing Items

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

This is the Errormessage i get, when I call the following Code:

ASPX:

<asp:FormView ID="FormView1" runat="server" DataSourceID="EntityDataSource1">
  <EditItemTemplate>
      <asp:DropDownList runat="server" ID="ddlUser" Selectedvalue='<%# Bind("UserID") %>' 
                        AppendDataBoundItems="true" DataSourceID="EntityDataSource2" 
                        DataTextField="UserName" DataValueField="UserID" >           
          <asp:ListItem></asp:ListItem>
       </asp:DropDownList>

<asp:EntityDataSource ID="EntityDataSource2" runat="server" EntitySetName="User" AutoGenerateWhereClause="true">
    <WhereParameters>
        <asp:Parameter Name="deleted" DefaultValue="false" Type="Boolean" />
    </WhereParameters>
</asp:EntityDataSource>

The reason for this issue is, that EntityDataSource2 only selects not deleted users. BUT selected users can be deleted any time and then they appear no longer in the result, which leads to this error.

I've searched for answers and only fond this one, which works but produces duplicate entries (because DataBind is called twice). Another discussion here without result.

Question: How can I prevent this error and select the empty item if the user was deleted in the meantime.


What i've tried so far:

First I thought of

protected void DropDownList1_DataBinding(object sender, EventArgs e)
{
    DropDownList DropDownList1 = (DropDownList)sender;    
    if(!Helper.CheckIfValid(DropDownList1.SelectedValue))
    {
       DropDownList1.SelectedValue = "";
    }
}

but the SelectedValue is not set at DataBinding event.

Upvotes: 1

Views: 274

Answers (2)

Trung Duong
Trung Duong

Reputation: 3475

This is a work around suggestion. I think this is not a clean solution but hope it could help.

The reason for this issue is that the selected item does not exist in the list of items. To prevent it happens, we should ensure that the selected item will always exist by doing as following.

We add an item that has the same value as selected value.

<asp:DropDownList runat="server" ID="ddlUser" Selectedvalue='<%# Bind("UserID") %>' 
                        AppendDataBoundItems="true" DataSourceID="EntityDataSource2" 
                        DataTextField="UserName" DataValueField="UserID" 
                        OnDataBound="OnListDataBound" >           
          <asp:ListItem Value="">(none)</asp:ListItem>
          <asp:ListItem Value='<%# Bind("UserID") %>'></asp:ListItem>
</asp:DropDownList>

Then on DataBound event, we do addition check to find out weither the selected value is valid or not.

  • If there is only one list item that has same value as selected value, it should be invalid selected value. We should remove it from list and reset selected value to empty.
  • If there are two items that have same value as selected value, we should remove the item which has empty text and set selected value to the remain item.
protected void OnListDataBound(object sender, EventArgs e) 
{
   // addition check
}

UPDATE
After revising the problem, seem I've been thinking too complicated. My above suggestion is also a bad solution.
Obviously, it's not valid to select an item that doesn't exist in the list. So instead trying to bind a value that could be invalid, why don't we remove it, and then select the correct value in code behind?

ASPX

<asp:DropDownList runat="server" ID="ddlUser" 
     AppendDataBoundItems="true" DataSourceID="EntityDataSource2" 
    DataTextField="UserName" DataValueField="UserID" 
    OnDataBound="OnListDataBound" >           
          <asp:ListItem Value="">(none)</asp:ListItem>
</asp:DropDownList>

Code-behind

protected void OnListDataBound(object sender, EventArgs e) 
{
    DropDownList dropdown = (DropDownList)sender;
    string userId = string.Empty;

    // Assign value for userId from FormView1.DataSource
    ....

    // Check to select valid item
    ListItem foundItem = dropdown.Items.FindByValue(userId); 
    if (foundItem != null)
    {
        dropdown.SelectedValue = userId;
    }
    else
    {
        dropdown.SelectedValue = string.Empty;
    }
}   

Upvotes: 0

Simon Mourier
Simon Mourier

Reputation: 139256

I suggest you define a custom control in your project (and reference it instead of asp:DropDownList) derived from the standard DrowDownList that will not throw when the selection is not defined as an item in the list.

If should work as is (by default, the empty value item will be selected), but you can also define what you consider as the value or the text that represent the "not selected" item using one of its properties. Here is the class:

public class ExtendedDropDownList : System.Web.UI.WebControls.DropDownList
{
    protected override void PerformDataBinding(IEnumerable dataSource)
    {
        try
        {
            base.PerformDataBinding(dataSource);
        }
        catch (ArgumentOutOfRangeException)
        {
            ListItem item;

            // try the value we defined as the one that represents <no selection>
            if (NoSelectionValue != null)
            {
                item = Items.FindByValue(NoSelectionValue);
                if (item != null)
                {
                    item.Selected = true;
                    return;
                }
            }

            // try the text we defined as the one that represents <no selection>
            if (NoSelectionText != null)
            {
                item = Items.FindByText(NoSelectionText);
                if (item != null)
                {
                    item.Selected = true;
                    return;
                }
            }

            // try the empty value if it exists
            item = Items.FindByValue(string.Empty);
            if (item != null)
            {
                item.Selected = true;
                return;
            }
        }
    }

    [DefaultValue(null)]
    public string NoSelectionValue
    {
        get
        {
            return (string)ViewState[nameof(NoSelectionValue)];
        }
        set
        {
            ViewState[nameof(NoSelectionValue)] = value;
        }
    }

    [DefaultValue(null)]
    public string NoSelectionText
    {
        get
        {
            return (string)ViewState[nameof(NoSelectionText)];
        }
        set
        {
            ViewState[nameof(NoSelectionText)] = value;
        }
    }
}

}

Upvotes: 1

Related Questions