ListBox для ArgumentNullException Имя параметра: источник - PullRequest
7 голосов
/ 22 августа 2011

Установка:

Я создал контроллер с помощью MvcScaffolding.

Для свойства Model.IdCurrencyFrom леса создали Html.DropDownListFor:

@Html.DropDownListFor(model => model.IdCurrencyFrom, 
    ((IEnumerable<FlatAdmin.Domain.Entities.Currency>)ViewBag.AllCurrencies).Select(option => new SelectListItem {
        Text = (option == null ? "None" : option.CurrencyName), 
        Value = option.CurrencyId.ToString(),
        Selected = (Model != null) && (option.CurrencyId == Model.IdCurrencyFrom)
    }), "Choose...")

Это прекрасно работает как с новыми записями, так и с редактированием существующих.

Проблема:

Существует только 3 валюты: AR $, US $ и GB £. Итак, вместо выпадающего списка я хочу ListBox.

Так что я изменил вышеприведенное на:

@Html.ListBoxFor(model => model.IdCurrencyFrom, 
    ((IEnumerable<FlatAdmin.Domain.Entities.Currency>)ViewBag.AllCurrencies).Select(option => new SelectListItem {
        Text = (option == null ? "None" : option.CurrencyName), 
        Value = option.CurrencyId.ToString(),
        Selected = (Model != null) && (option.CurrencyId == Model.IdCurrencyFrom)
    }))

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

Вопросы:

Что происходит?!

Ничего не изменилось. Переключение обратно на DropDownListFor и все работает нормально. Переключение на ListBox (в отличие от ListBoxFor) и я получаю сообщение об ошибке.

Модель не является нулевой (как я уже сказал, она отлично работает с DropDownListFor) ... и у меня закончились идеи.

1 Ответ

6 голосов
/ 16 октября 2011

Я проверил источник HTML-помощников, это было забавное упражнение.

TL; DR; Проблема в том, что ListBoxFor предназначен для множественного выбора и ожидает перечислимое свойство Model. Свойство Model (model.IdCurrencyFrom) не является перечисляемым, поэтому вы получаете исключение.

Вот мои выводы:

  1. Метод ListBoxFor всегда отображает элемент select с атрибутом multiple="multiple". Это жестко закодировано в System.Web.Mvc.Html.SelectExtensions

    private static MvcHtmlString ListBoxHelper(HtmlHelper htmlHelper, string name, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes) {
        return SelectInternal(htmlHelper, null /* optionLabel */, name, selectList, true /* allowMultiple */, htmlAttributes);
    }
    

    Так что, может быть, вы все равно не хотите разрешать пользователю несколько валют ...

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

    object defaultValue = (allowMultiple) ? htmlHelper.GetModelStateValue(fullName, typeof(string[])) : htmlHelper.GetModelStateValue(fullName, typeof(string)); 
    

    Это работает для DropDownList, потому что оно передает false в allowMultiple при вызове SelectInternal.
    Поскольку ваш ViewData.ModelState пуст (так как в контроллере раньше не было проверки), defaultValue будет null. Затем defaultValue инициализируется значением вашей модели по умолчанию (ваш случай model.IdCurrencyFrom равен int, я полагаю), так что это будет 0. :

    if (!usedViewData) {
            if (defaultValue == null) {
                defaultValue = htmlHelper.ViewData.Eval(fullName);
            } 
     }
    

    Мы приближаемся к исключению :) Потому что, как я уже упоминал, ListBoxFor поддерживает только множественный выбор, поэтому он пытается обработать defaultValue как IEnumbrable:

    IEnumerable defaultValues = (allowMultiple) ? defaultValue as IEnumerable : new[] { defaultValue };
    IEnumerable<string> values = from object value in defaultValues select Convert.ToString(value, CultureInfo.CurrentCulture); 
    

    А во второй строке есть ваше ArgumentException, потому что defaultValues равно null.

  3. Поскольку он ожидает, что defaultValue будет перечислимым, и потому что перечислимая строка. Если вы измените тип model.IdCurrencyFrom на string, он будет работать. Но, конечно, у вас будет многократный выбор в пользовательском интерфейсе, но вы получите только первый выбор в вашей модели.

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