Почему 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 будет работать. Я не понимаю, почему это может вызвать проблему (кроме того, что она соответствует имени сущности), и я недостаточно хорош в этом, чтобы иметь возможность копаться и узнавать.
Ответы
Ответ 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 - у модели, с которой вы привязываетесь, имеет приоритет, даже если вы делаете!