ASP.NET MVC Html.DropDownList SelectedValue - PullRequest
       26

ASP.NET MVC Html.DropDownList SelectedValue

100 голосов
/ 09 марта 2009

Я пробовал это RC1, а затем обновился до RC2, что не решило проблему.

// in my controller
ViewData["UserId"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

результат: свойство SelectedValue установлено для объекта

// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["UserId"])%>

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

Как мне это сделать?

Обновление Благодаря ответу Джона Феминеллы я узнал, в чем проблема. «UserId» - это свойство в модели, для которой строго типизирован мой вид. Когда Html.DropDownList («UserId» изменяется на любое другое имя, кроме «UserId», выбранное значение отображается правильно.

Это приводит к тому, что значение не привязывается к модели.

Ответы [ 8 ]

69 голосов
/ 29 июля 2010

Вот как я исправил эту проблему:

У меня было следующее:

Контроллер:

ViewData["DealerTypes"] = Helper.SetSelectedValue(listOfValues, selectedValue) ;

View

<%=Html.DropDownList("DealerTypes", ViewData["DealerTypes"] as SelectList)%>

Изменено:

Просмотр

<%=Html.DropDownList("DealerTypesDD", ViewData["DealerTypes"] as SelectList)%>

Похоже, что DropDown не должен иметь того же имени, что и имя ViewData: S странно, но это работало.

50 голосов
/ 09 марта 2009

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

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

А потом:

var list = new[] {   
    new Person { Id = 1, Name = "Name1" }, 
    new Person { Id = 2, Name = "Name2" }, 
    new Person { Id = 3, Name = "Name3" } 
};

var selectList = new SelectList(list, "Id", "Name", 2);
ViewData["People"] = selectList;

Html.DropDownList("PeopleClass", (SelectList)ViewData["People"])

С MVC RC2 я получаю:

<select id="PeopleClass" name="PeopleClass">
    <option value="1">Name1</option>
    <option selected="selected" value="2">Name2</option>
    <option value="3">Name3</option>
</select>
5 голосов
/ 11 августа 2012

Вы все еще можете назвать DropDown как "UserId", и при этом привязка модели будет работать правильно.

Единственное требование, чтобы это работало, это то, что ключ ViewData, который содержит SelectList, не имеет того же имени, что и свойство Model, которое вы хотите связать. В вашем конкретном случае это будет:

// in my controller
ViewData["Users"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId", (SelectList)ViewData["Users"])%>

В результате будет создан элемент select с именем UserId, имя которого совпадает с именем свойства UserId в вашей модели, и, следовательно, связыватель модели установит для него значение, выбранное в элементе выбора html, сгенерированном помощником Html.DropDownList. .

Я не уверен, почему этот конкретный конструктор Html.DropDownList не будет выбирать значение, указанное в SelectList, когда вы помещаете список выбора в ViewData с ключом, равным имени свойства. Я подозреваю, что это как-то связано с использованием помощника DropDownList в других сценариях, где существует соглашение, что у вас есть SelectList в ViewData с тем же именем, что и у свойства вашей модели. Это будет работать правильно:

// in my controller
ViewData["UserId"] = new SelectList(
    users, 
    "UserId", 
    "DisplayName", 
    selectedUserId.Value); // this has a value

// in my view
<%=Html.DropDownList("UserId")%>
2 голосов
/ 13 декабря 2013

Если мы не думаем, что это ошибка, которую должна исправить команда, MSDN при аренде должен улучшить документ. Заблуждение действительно исходит из плохого документа об этом. В MSDN объясняются параметры name as,

Type: System.String
The name of the form field to return.

Это просто означает, что конечный html, который он генерирует, будет использовать этот параметр в качестве имени выбранного входа. Но на самом деле это означает нечто большее.

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

Используйте приведенный выше пример,

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

Если мы следуем предположению, мы должны определить модель представления для этого связанного представления раскрывающегося списка

public class PersonsSelectViewModel{
    public string SelectedPersonId,
    public List<SelectListItem> Persons;
}

Поскольку при отправке обратно, только выбранное значение будет отправлено обратно, поэтому предполагается, что она должна отправлять обратно в свойство модели SelectedPersonId, что означает, что первый параметр Html.DropDownList name должен быть 'SelectedPersonId'. Таким образом, дизайнер считает, что при отображении представления модели в представлении свойство модели SelectedPersonId должно содержать значение по умолчанию для этого раскрывающегося списка. Даже если ваш список людей уже установил флаг Selected, чтобы указать, какой из них выбран / по умолчанию, tml.DropDownList фактически проигнорирует это, перестроит свой собственный IEnumerable и установит элемент по умолчанию / выбранный на основе имени.

Вот код из asp.net mvc

private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMetadata metadata,
            string optionLabel, string name, IEnumerable<SelectListItem> selectList, bool allowMultiple,
            IDictionary<string, object> htmlAttributes)
{
    ...

    bool usedViewData = false;

    // If we got a null selectList, try to use ViewData to get the list of items.
    if (selectList == null)
    {
        selectList = htmlHelper.GetSelectData(name);
        usedViewData = true;
    }

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

    // If we haven't already used ViewData to get the entire list of items then we need to
    // use the ViewData-supplied value before using the parameter-supplied value.
    if (defaultValue == null && !String.IsNullOrEmpty(name))
    {
        if (!usedViewData)
        {
            defaultValue = htmlHelper.ViewData.Eval(name);
        }
        else if (metadata != null)
        {
            defaultValue = metadata.Model;
        }
    }

    if (defaultValue != null)
    {
        selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
    }

    ...

    return tagBuilder.ToMvcHtmlString(TagRenderMode.Normal);
}

Итак, код на самом деле пошел дальше, он не только пытается найти имя в модели, но также и в данных представления, как только он его находит, он перестраивает список selectList и игнорирует ваш исходный Selected.

Проблема в том, что во многих случаях мы не используем это таким образом. мы просто хотим добавить selectList с одним / несколькими элементами. Выбранный набор true.

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

Но я все еще думаю, что mvc должен улучшить его, добавив еще одно условие

if ((defaultValue != null) && (!selectList.Any(i=>i.Selected)))
{
    selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}

Потому что, если в исходном списке selectList уже был один объект Selected, почему бы вам его игнорировать?

Только мои мысли.

2 голосов
/ 30 декабря 2011

Код в предыдущем посте MVC 3 не работает, но это хорошее начало. Я починю это. Я протестировал этот код и он работает в MVC 3 Razor C #. Этот код использует шаблон ViewModel для заполнения свойства, которое возвращает List<SelectListItem>.

Модель класса

public class Product
{
    public string Name { get; set; }
    public decimal Price { get; set; }
}

Класс ViewModel

using System.Web.Mvc;

public class ProductListviewModel
{
    public List<SelectListItem> Products { get; set; }
}

Метод Контроллера

public ViewResult List()
{
    var productList = new List<SelectListItem>();

    foreach (Product p in Products)
    {
        productList.Add(new SelectListItem
        {
            Value = p.ProductId.ToString(),
            Text = "Product: " + p.Name + " " + p.Price.ToString(),
            // To set the selected item use the following code 
            // Note: you should not set every item to selected
            Selected = true
        });
    }

    ProductListViewModel productListVM = new ProductListViewModeld();

    productListVM.Products = productList;

    return View(productListVM);
}

Вид

@model MvcApp.ViewModels.ProductListViewModel

@using (Html.BeginForm())
{
    @Html.DropDownList("Products", Model.Products)
}

Вывод HTML будет выглядеть примерно так:

<select id="Products" name="Products">
    <option value="3">Product: Widget 10.00</option>
    <option value="4">Product: Gadget 5.95</option>
</select>

в зависимости от того, как вы форматируете вывод. Надеюсь, это поможет. Код работает.

1 голос
/ 30 июня 2010

Похоже, это ошибка в классе SelectExtensions, поскольку он будет проверять только ViewData, а не модель для выбранного элемента. Таким образом, хитрость заключается в том, чтобы скопировать выбранный элемент из модели в коллекцию ViewData под именем свойства.

Это взято из ответа, который я дал на форумах MVC, у меня также есть более полный ответ в сообщении блога , в котором используется атрибут KazD DropDownList ...

Учитывая модель

public class ArticleType
{
   public Guid Id { get; set; }
   public string Description { get; set; }
}

public class Article
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public ArticleType { get; set; }
}

и базовый вид модели

public class ArticleModel
{
     public Guid Id { get; set; }
     public string Name { get; set; }

     [UIHint("DropDownList")]
     public Guid ArticleType { get; set; }
}

Затем мы пишем шаблон редактора DropDownList следующим образом.

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script runat="server">  
    IEnumerable<SelectListItem> GetSelectList()
    {
        var metaData = ViewData.ModelMetadata;
        if (metaData == null)
        {
            return null;
        }

        var selected = Model is SelectListItem ? ((SelectListItem) Model).Value : Model.ToString();
        ViewData[metaData.PropertyName] = selected;

        var key = metaData.PropertyName + "List";
        return (IEnumerable<SelectListItem>)ViewData[key];
    }
</script>
<%= Html.DropDownList(null, GetSelectList()) %>

Это также будет работать, если вы измените ArticleType в модели представления на SelectListItem, хотя вам необходимо реализовать преобразователь типов в соответствии с блогом Kazi и зарегистрировать его, чтобы связыватель воспринял это как простой тип.

В вашем контроллере у нас есть ...

public ArticleController 
{
     ...
     public ActionResult Edit(int id)
     {
          var entity = repository.FindOne<Article>(id);
          var model = builder.Convert<ArticleModel>(entity);

          var types = repository.FindAll<ArticleTypes>();
          ViewData["ArticleTypeList"] = builder.Convert<SelectListItem>(types);

          return VIew(model);
     }
     ...
}
0 голосов
/ 29 ноября 2011

В ASP.NET MVC 3 вы можете просто добавить свой список в ViewData ...

var options = new List<SelectListItem>();

options.Add(new SelectListItem { Value = "1", Text = "1" });
options.Add(new SelectListItem { Value = "2", Text = "2" });
options.Add(new SelectListItem { Value = "3", Text = "3", Selected = true });

ViewData["options"] = options;

... а затем назовите его по имени в вашем бритвенном представлении ...

@Html.DropDownList("options")

Вам не нужно вручную «использовать» список в вызове DropDownList. Делая это таким образом, правильно установите выбранное значение и для меня.

Отказ от ответственности:

  1. Не пробовал это с механизмом просмотра веб-форм, но он тоже должен работать.
  2. Я не проверял это в v1 и v2, но это может сработать.
0 голосов
/ 10 октября 2011

Проблема заключается в том, что раскрывающиеся окна не работают так же, как списки, по крайней мере так, как этого требует дизайн ASP.NET MVC2: в раскрывающемся списке допускается только ноль или одно значение, поскольку списки могут иметь выбор из нескольких значений. Таким образом, строго придерживаясь HTML, это значение должно быть не в списке опций как флаг «selected», а в самом вводе.

См. Следующий пример:

<select id="combo" name="combo" value="id2">
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

<select id="listbox" name="listbox" multiple>
  <option value="id1">This is option 1</option>
  <option value="id2" selected="selected">This is option 2</option>
  <option value="id3">This is option 3</option>
</select>

Комбо имеет выбранную опцию, но также имеет свой атрибут значения, установленный . Таким образом, если вы хотите, чтобы ASP.NET MVC2 отображал выпадающий список, а также выбрал определенное значение (т. Е. Значения по умолчанию и т. Д.), Вы должны указать ему значение при рендеринге, например:

// in my view             
<%=Html.DropDownList("UserId", selectListItems /* (SelectList)ViewData["UserId"]*/, new { @Value = selectedUser.Id } /* Your selected value as an additional HTML attribute */)%>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...