ASP.NET MVC Html.DropDownList SelectedValue


103

Я пробовал, что это 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", выбранное значение отображается правильно.

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


Вы уверены, что значение есть в списке?
Джон Шиэн,

Проблема все еще существует в версии 1 ASP.NET MVC
Матиас Ф,

Что такое selectedUserId !?
fulvio

Как вы предполагаете связать значение при обновлении, если имя вашего ввода не совпадает с вашим свойством?
ryudice

Ответы:


68

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

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

Контроллер:

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

Посмотреть

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

Изменено на следующее:

Посмотреть

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

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


19
+1 за «DropDown не должен иметь то же имя, что и имя ViewData» Большое спасибо
Дэниел Дайсон

Спасибо!!! Вы только что решили мою проблему после многих часов размышлений, почему это не сработало :)
Джен

1
Это работает для выбранного значения, но проблема возникает при попытке обновить "DealerTypes" в базе данных, поскольку DropDownList теперь привязан к "DealerTypesDD", которого нет в модели. Обходной путь - добавить скрытое поле и атрибуты htmlAttributes в DropDownList: <input type = "hidden" name = "DealerTypes" id = "DealerTypes" value = "" /> <% = Html.DropDownList ("DealerTypesDD", ViewData [ "DealerTypes"] как SelectList, новый {@onchange = "DealerTypes.value = this.value"})%>
Зим

3
Лучше добавить DS к имени viewdata, чтобы не
нарушить

1
Какого черта! Это не исправлено в MVC5 ??? В качестве еще одной уловки, упомянутой @Paul Hatcher, скопируйте выбранный идентификатор в ViewData ["DealerTypes"].
jsgoupil

51

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

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>

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

первый в модели? второй в контроллере? и третье на виду понятно, а вот 2 первых .. ???
rr

Хороший ответ, но не лучше ли использовать ViewModel?
Jess

@Jess: Я написал этот ответ в марте 2009 года, более 5 лет назад. Времена изменились! Не стесняйтесь обновлять. :)
John Feminella

5

Вы по-прежнему можете назвать 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"])%>

В результате будет создан элемент выбора с именем UserId, имя которого совпадает с именем свойства UserId в вашей модели, и поэтому связыватель модели установит для него значение, выбранное в элементе select 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")%>

Большое спасибо, приятель, прежде всего ответы, это сработало для меня, не знаю почему, но оно сработало
Ламин Санне

В заключение! После нескольких часов попыток я наконец нашел ваше «единственное требование». Это сделало это.
SteveCav

2

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

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

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

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

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

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 по имени должно быть «SelectedPersonId». Таким образом, дизайнер считает, что при отображении вида модели в представлении свойство модели SelectedPersonId должно содержать значение по умолчанию для этого раскрывающегося списка. Даже если подумать, что ваши List <SelectListItem> Persons уже установили флаг Selected, чтобы указать, какой из них выбран / по умолчанию, tml.DropDownList фактически проигнорирует это и перестроит свой собственный IEnumerable <SelectListItem> и установит элемент по умолчанию / выбранный на основе имени.

Вот код из 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 с одним / несколькими элементами.

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

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

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

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

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


Это действительно лучшее объяснение, которое я когда-либо читал. Избавил меня от головной боли. Все, что мне нужно было сделать, это установить значение свойства viewmodel, и это сработало. Спасибо.
Моисей Мачуа

2

Код из предыдущего поста 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
Это не устанавливает selectedвариант.
Jess

Чтобы установить выбранный элемент с помощью SelectListItem, установите для свойства Selected значение true.
wayne.blackmon

1

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

Это взято из ответа, который я дал на форумах MVC, у меня также есть более полный ответ в сообщении в блоге, в котором используется атрибут Kazi 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, хотя вам нужно реализовать преобразователь типов в соответствии с блогом Кази и зарегистрировать его, чтобы связыватель рассматривал его как простой тип.

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

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);
     }
     ...
}

Спасибо за ответ, я должен это проверить.
blu

0

Проблема в том, что выпадающие окна не работают так же, как списки, по крайней мере, так, как ожидает дизайн ASP.NET MVC2: Dropbox допускает только ноль или одно значение, так как списки могут иметь выбор из нескольких значений. Таким образом, в строгом соответствии с 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 */)%>

0

В 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

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

@Html.DropDownList("Example", new SelectList(Model.FeeStructures, "Id", "NameOfFeeStructure", Model.Matters.FeeStructures))

Model.Matters.FeeStructures выше - это мой идентификатор, который может быть вашим значением элемента, который следует выбрать.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.