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
Mathias F

Що вибраноUserId !?
фульвіо

Як ви припускаєте прив'язувати значення оновлення, якщо назва вашого входу не збігається з вашою власністю?
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" Велика подяка
Даніель Дайсон

Дякую!!! Ви просто вирішили мою проблему після багатьох годин, коли цікавились, чому це не працює :)
Jen

1
Це працює для вибраного значення, але проблема виникає при спробі оновити "DealerTypes" в db, оскільки саме DropDownList тепер пов'язаний з "DealerTypesDD", якого немає в моделі. Вирішення питання полягає в тому, щоб додати приховане поле та htmlAttributes до DropDownList: <введення типу = "приховане" ім'я = "DealerTypes" id = "DealerTypes" value = "" /> <% = Html.DropDownList ("DealerTypesDD", ViewData [ "DealerTypes"] як SelectList, новий {@onchange = "DealerTypes.value = this.value"})%>
Zim

3
Вам краще додати "DS" до назви viewdata, тим самим не руйнуючи прив'язку моделі ...
noocyte

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: Цю відповідь я написав у березні 2009 року, понад 5 років тому. Часи змінилися! Не соромтеся оновлюватись. :)
Джон Фемінелла

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 у вашій моделі, і тому в'яжуча модель встановить його зі значенням, вибраним у елементі вибору html, генерованому помічником Html.DropDownList.

Я не впевнений, чому саме цей конструктор Html.DropDownList не вибере значення, вказане в SelectList, коли ви кладете список вибору у ViewData з ключем, рівним імені властивості. Я підозрюю, що це має щось спільне з тим, як помічник DropDownList використовується в інших сценаріях, де умовою є те, що у ViewData ви маєте SelectList з тим самим іменем, що і властивість у вашій моделі. Це буде працювати правильно:

// 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 має містити значення за замовчуванням цього випадаючого списку. Навіть думав, що ваш список <SelectListItem> Persons вже встановив вибраний прапор, щоб вказати, який з них вибрано / за замовчуванням, 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);
}

Таким чином, код фактично пішов далі, він не тільки намагається шукати ім'я в моделі, але і в viewdata, як тільки він знайде його, він відновить 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 для заселення властивості, яка повертає a 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варіант.
Джесс

Щоб встановити вибраний елемент за допомогою SelectListItem, встановіть для вибраного властивості значення true.
wayne.blackmon

1

Здається, це помилка в класі SelectExtensions, оскільки вона перевірятиме лише ViewData, а не модель для вибраного елемента. Тому фокус полягає в тому, щоб скопіювати вибраний елемент з моделі в колекцію ViewData під назвою властивості.

Це взято з відповіді, яку я дав на форумах MVC, у мене також є більш повна відповідь у публікації в блозі, в якій використовується атрибут DropDownList Kazi ...

Дано модель

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

Проблеми полягають у тому, що папки не працюють так само, як списки списку, принаймні так, як очікує дизайн ASP.NET MVC2: Панель для випуску дозволяє лише нульове або одне значення, оскільки списки списку можуть мати вибір декількох значень. Отже, будучи суворим з HTML, це значення має бути не у списку параметрів як "вибраний" прапор, а у самому введенні.

Дивіться наступний приклад:

<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 візуалізував Dropbox, а також вибрав конкретне значення (тобто значення за замовчуванням тощо), вам слід надати йому значення при візуалізації, наприклад:

// 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.