Подтвердить что ты не робот

Почему ListBoxFor не выбирает элементы, но ListBox?

У меня есть следующий код на мой взгляд:

<%= Html.ListBoxFor(c => c.Project.Categories,
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

<%= Html.ListBox("MultiSelectList", 
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

Единственное отличие состоит в том, что первый помощник строго типизирован (ListBoxFor), а не отображает выбранные элементы (1,2), даже если элементы отображаются в списке и т.д. Более простой ListBox работает как ожидалось.

Мне явно не хватает чего-то здесь. Я могу использовать второй подход, но это действительно подтачивает меня, и я хотел бы понять это.

Для справки, моя модель:

public class ProjectEditModel
{
    public Project Project { get; set; }
    public IEnumerable<Project> Projects { get; set; }
    public IEnumerable<Client> Clients { get; set; }
    public IEnumerable<Category> Categories { get; set; }
    public IEnumerable<Tag> Tags { get; set; }
    public ProjectSlide SelectedSlide { get; set; }
}

Update

Я просто изменил имя ListBox на Project.Categories(совпадаю с моей моделью), и теперь он НЕ ВЫБИРАЕТСЯ, чтобы выбрать элемент.

<%= Html.ListBox("Project.Categories",
        new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

Я, очевидно, не понимаю магию, которая здесь происходит.

Обновление 2

Хорошо, это чисто имя, например, это работает...

<%= Html.ListBox("Project_Tags",
new MultiSelectList(Model.Tags, "Id", "Name", Model.Project.Tags.Select(t => t.Id)))%>

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

4b9b3361

Ответ 1

Я сам наткнулся на эту проблему, наконец, я понял, что проблема заключается в соглашении об именах.

Вы не можете назвать виджеты ViewBag или ViewData, содержащие SelectList или MultiSelectList, с тем же именем, что и ваша модель свойств, содержащая выбранные элементы. По крайней мере, если вы используете помощник ListBoxFor или DropDownListFor.

Вот пример:

    public class Person
    {
          public List<int> Cars { get; set; }
    }

    [HttpGet]
    public ActionResult Create()
    {
          //wont work
          ViewBag.Cars = new SelectList(carsList, "CarId", "Name"); 

          //will work due to different name than the property.
          ViewBag.CarsList = new SelectList(carsList, "CarId", "Name"); 

          return View();
    }

    //View
    @Html.ListBoxFor(model => model.Cars, ViewBag.CarsList as SelectList)

Я уверен, что у меня много других способов сделать это, но это решило мою проблему, надеюсь, что это поможет кому-то!

Ответ 2

Я тоже застрял в этой же проблеме и столкнулся с той же проблемой с ListBox и ListBoxFor.

Независимо от того, что я делаю, я не могу получить выбор в ListBoxFor. Если я перейду в ListBox и назову его чем-то иным, чем имя свойства данных, к которым я привязываю, будут выполняться выборы.

Но тогда, потому что я не использую ListBoxFor, и данные, например, сидят внутри класса модели (Model.Departments), я не получаю привязку модели на обратном пути к моему контроллеру, и, следовательно, свойство равно null.

EDIT Я нашел решение, размещенное кем-то еще здесь; Проблемы с выбором значений в ListBoxFor

Ответ 3

Правильный ответ заключается в том, что он работает не очень хорошо. Поэтому я прочитал код MVC. Что вам нужно сделать, это реализовать IConvertible, а также создать TypeConverter.

Итак, в моем случае у меня был класс Country, чтобы люди могли выбирать из списка стран. Никакой радости в его выборе. Я ожидал, что объект сравним с selectedItems с listitems, но нет, это не так, как это работает. Несмотря на то, что MultiListItem работает и правильно получает выбранные элементы, момент, когда он привязан к вашей модели, все это основано на проверке того, что строка, представляющая ваш экземпляр объекта, соответствует строке "value" (или имя, если это отсутствует) в списке элементов в SelectItemList.

Итак, реализуем IConvertible, возвращаем строковое значение из ToString, которое будет соответствовать значению в SelectItemList. например, в моем случае CountryCode был сериализован в свойство SelectItem Value, поэтому в ToString IConvertible я вернул CountryCode. Теперь все правильно выбирается.

Я укажу, что TypeConverter используется по пути в. На этот раз его обратный. Этот CountryCode возвращается, а "EN" нуждается в преобразовании в экземпляр класса Country. То, что вошло TypeConverter. Это также о том, как я понял, насколько сложно использовать этот подход.

p.s, поэтому в вашем классе Category вам нужно реализовать IConvertible. Если это из структуры сущности в качестве моей компании, тогда вам нужно будет использовать частичный класс для реализации IConvertible и реализовать ToString и украсить его с помощью TypeConverter, который вы написали тоже.

Ответ 4

Кроме того, вы можете попытаться очистить ModelState для c.Project.Categories в контроллере:

[HttpPost]
public ActionResult Index(ModelType model)
{
    ModelState.Remove("Project.Categories");
    return View("Index", model);
}

И используйте следующую конструкцию:

<%= Html.ListBoxFor(c => c.Project.Categories,
        new MultiSelectList(Model.Categories, "Id", "Name"))%>

Где c.Project.Categories IEnumerable<int>.

Извините за мой английский. Удачи!

Ответ 5

Хотя это не ответ на ваш основной вопрос, стоит отметить, что когда MVC генерирует имена, он превращает что-то вроде Project.Tags в Project_Tags, заменяя периоды символами подчеркивания.

Причина, по которой это происходит, состоит в том, что период в идентификаторе элемента будет выглядеть как элемент с именем Project с классом тегов в CSS. Очевидно, что это плохо, поэтому перевод подчеркивает, что поведение предсказуемо.

В первом примере

<%= Html.ListBoxFor(c => c.Project.Categories,
    new MultiSelectList(Model.Categories, "Id", "Name", new List<int> { 1, 2 }))%>

listbox пытается привязать к Model.Project.Categories для вашей строго типизированной модели, которая была предоставлена ​​на странице (с использованием лямбда-нотации). Я не уверен, что делает второй параметр в ListBoxFor.

Какова модель, которая передается на страницу?

Ответ 6

Попробуйте это

<%= Html.ListBoxFor(c => c.Project.Categories,
    new MultiSelectList(
        Model.Categories
        ,"Id"
        ,"Name"
        ,Model.Project.Tags.Select(
        x => new SelectListItem()
            {
            Selected = true,
            Text = x.TEXT,
            Value = x.ID.ToString()
            }).ToList())

       )
)%>

Ответ 7

Html.ListboxFor и Html.Listbox отлично работают, когда вы НЕ привязываете окно списка к источнику данных. Я предполагаю, что это предполагаемое использование:

// Model
public class ListBoxEditModel
{
    public IEnumerable<Category> Categories { get; set; }
    public IEnumerable<Category> SelectedCategories { get; set; }
}    

В представлении:

@Html.ListBoxFor(m => m.SelectedCategories,
                 new MultiSelectList(Model.Categories, "Id", "Name"))
// or, equivalently
@Html.ListBox("SelectedCategories" ,
                 new MultiSelectList(Model.Categories, "Id", "Name"))

Обратите внимание, что в этом случае вам не нужно явно указывать, какие значения выбраны в MultiSelectList - у модели, с которой вы привязываетесь, имеет приоритет, даже если вы делаете!