При публикации раскрывающийся список SelectList.SelectedValue имеет значение null

Моя модель выглядит следующим образом:

public class testCreateModel
{
    public string s1 { get; set; }
    public SelectList DL { get; set; }

    public testCreateModel()
    {
        Dictionary<string, string> items = new Dictionary<string, string>();
        items.Add("1", "Item 1");
        items.Add("2", "Item 2");
        DL = new SelectList(items, "Key", "Value");
    }
}

Мои исходные действия:

    public ActionResult testCreate()
    {
        testCreateModel model = new testCreateModel();
        return View(model);
    }

Мое представление Razor (нерелевантные части удалены):

@model Tasks.Models.testCreateModel

@using (Html.BeginForm()) {
<fieldset>
    <legend>testCreateModel</legend>

    <div class="editor-label">
        @Html.LabelFor(model => model.s1)
    </div>
    <div class="editor-field">
        @Html.EditorFor(model => model.s1)
    </div>

    <div class="editor-label">
        Select an item:
    </div>
    <div class="editor-field">
        @Html.DropDownList("dropdownlist", (SelectList)Model.DL)
    </div>

    <p>
        <input type="submit" value="Create" />
    </p>
</fieldset>
}

Действие обратной публикации:

    public ActionResult testCreate(testCreateModel model, FormCollection collection)
    {
        if (ModelState.IsValid)
        {
            Console.WriteLine("SelectedValue: ",model.DL.SelectedValue);
            Console.WriteLine("FormCollection:", collection["dropdownlist"]);
            // update database here...
        }
        return View(model);
    }

При обратной отправке model.DL.SelectedValue имеет значение null. (Впрочем, выбранный элемент можно получить из FormCollection, но это не главное). В противном случае объект DL по-прежнему правильно заполнен, вывод Immediate Window выглядит следующим образом:

model.DL
{System.Web.Mvc.SelectList}
    base {System.Web.Mvc.MultiSelectList}: {System.Web.Mvc.SelectList}
    SelectedValue: null
model.DL.Items
Count = 2
    [0]: {[1, Item 1]}
    [1]: {[2, Item 2]}
model.DL.SelectedValue
null

Q1: Как я могу вместо этого использовать свойство SelectedValue?

Теперь, если в представлении Razor я изменю имя тега Html SELECT на DL (то есть такое же, как имя свойства в модели):

@Html.DropDownList("DL", (SelectList)Model.DL)

У меня исключение:

No parameterless constructor defined for this object. 
Stack Trace: 
[MissingMethodException: No parameterless constructor defined for this object.]
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98
System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241
System.Activator.CreateInstance(Type type, Boolean nonPublic) +69
System.Web.Mvc.DefaultModelBinder.CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) +199
System.Web.Mvc.DefaultModelBinder.BindSimpleModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ValueProviderResult 
...

Q2: Почему?

Спасибо.


person Old Geezer    schedule 12.04.2012    source источник


Ответы (2)


MVC вернет только значение выбранной опции в вашем POST, поэтому вам нужно свойство, содержащее единственное возвращаемое значение.

В качестве хорошего совета попробуйте настроить SelectLists через ViewBag, что поможет очистить ваши ViewModels от данных, необходимых для заполнения формы.

Итак, ваш пример можно решить так:

public class testCreateModel
{
    public string s1 { get; set; }
    public int SelectedValue { get; set; }
}

и в вашем представлении просто сделайте это:

@Html.DropDownList("SelectedValue", (SelectList)ViewBag.DL)

перед заполнением ViewBag.DL в вашем действии GET.

Что касается вашего Q2, ModelBinder по умолчанию требует, чтобы все типы для привязки имели конструктор по умолчанию (чтобы ModelBinder мог их создавать)

person Leonardo Garcia Crespo    schedule 12.04.2012
comment
Благодарим вас за предложенный метод использования раскрывающегося списка. Это работает, и требуется меньше работы, чтобы подобрать выбранное значение! - person Old Geezer; 12.04.2012
comment
Спасибо, это лучший пост, который помог мне использовать раскрывающийся список в mvc. - person Bashar Kokash; 05.07.2012

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

В верхней части моего представления я указываю свою модель представления:

@model MyProject.ViewModels.MyViewModel

На мой взгляд, у меня есть раскрывающийся список, в котором отображаются все банки, из которых может выбрать пользователь:

<table>
     <tr>
          <td><b>Bank:</b></td>
          <td>
               @Html.DropDownListFor(
                    x => x.BankId,
                    new SelectList(Model.Banks, "Id", "Name", Model.BankId),
                    "-- Select --"
               )
               @Html.ValidationMessageFor(x => x.BankId)
          </td>
     </tr>
</table>

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

public class MyViewModel
{
     // Other properties

     public int BankId { get; set; }
     public IEnumerable<Bank> Banks { get; set; }
}

Моя модель банковского домена:

public class Bank
{
     public int Id { get; set; }
     public string Name { get; set; }
}

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

public ActionResult MyActionMethod()
{
     MyViewModel viewModel = new ViewModel
     {
          // Database call to get all the banks
          // GetAll returns a list of Bank objects
          Banks = bankService.GetAll()
     };

     return View(viewModel);
}

[HttpPost]
public ActionResult MyActionMethod(MyViewModel viewModel)
{
    // If you have selected an item then BankId would have a value in it
}

Надеюсь, это поможет.

person Brendan Vogt    schedule 12.04.2012
comment
Отлично и спасибо, что поделились. Однако у меня возникают проблемы с получением данных при обратной передаче формы. Любые идеи? Спасибо - person Shin Kazama; 21.12.2015