Как я могу заставить этот ASP.NET MVC SelectList работать? - PullRequest
124 голосов
/ 23 апреля 2009

Я создаю список выбора в моем контроллере для отображения в представлении.

Я пытаюсь создать его на лету, что-то вроде того ... вот так ...

myViewData.PageOptionsDropDown = 
   new SelectList(new [] {"10", "15", "25", "50", "100", "1000"}, "15");

Компилируется, но вывод плохой ...

<select id="PageOptionsDropDown" name="PageOptionsDropDown">
    <option>10</option>
    <option>15</option>
    <option>25</option>
    <option>50</option>
    <option>100</option>
    <option>1000</option>
</select>

Обратите внимание, как не выбран ни один элемент?

Как я могу это исправить ??

Ответы [ 23 ]

126 голосов
/ 23 апреля 2009

Вот как я это делаю

IList<Customer> customers = repository.GetAll<Customer>();
IEnumerable<SelectListItem> selectList = 
    from c in customers
    select new SelectListItem
    {
        Selected = (c.CustomerID == invoice.CustomerID),
        Text = c.Name,
        Value = c.CustomerID.ToString()
    };

На второй взгляд, я не уверен, что знаю, что вы после ...

68 голосов
/ 18 мая 2009

Я использую метод расширения:

использование

var departmentItems = departments.ToSelectList(d => d.Code + 
                                               " - " + d.Description,
                                               d => d.Id.ToString(),
                                               " - ");

var functionItems = customerFunctions.ToSelectList(f => f.Description, 
                                                   f => f.Id.ToString(), 
                                                   " - ");

с

public static class MCVExtentions
{
    public static List<SelectListItem> ToSelectList<T>(
        this IEnumerable<T> enumerable, 
        Func<T, string> text, 
        Func<T, string> value, 
        string defaultOption)
    {
        var items = enumerable.Select(f => new SelectListItem()
                                     {
                                         Text = text(f), 
                                         Value = value(f) 
                                     }).ToList();
        items.Insert(0, new SelectListItem()
                    {
                        Text = defaultOption, 
                        Value = "-1" 
                    });
        return items;
    }
}
48 голосов
/ 23 апреля 2009

Использование конструктора, который принимает items, dataValueField, dataTextField, selectedValue в качестве параметров:

ViewData["myList"] = 
                new SelectList(new[] { "10", "15", "25", "50", "100", "1000" }
                .Select(x => new {value = x, text = x}), 
                "value", "text", "15");

Тогда по вашему мнению:

<%=Html.DropDownList("myList") %>
18 голосов
/ 04 марта 2011

Основываясь на ответе Томаса Сток, я создал эти перегруженные ToSelectList методы:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

public static partial class Helpers
{
    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, bool selectAll = false)
    {
        return enumerable.ToSelectList(value, value, selectAll);
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, object selectedValue)
    {
        return enumerable.ToSelectList(value, value, new List<object>() { selectedValue });
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, IEnumerable<object> selectedValues)
    {
        return enumerable.ToSelectList(value, value, selectedValues);
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, bool selectAll = false)
    {
        foreach (var f in enumerable.Where(x => x != null))
        {
            yield return new SelectListItem()
            {
                Value = value(f).ToString(),
                Text = text(f).ToString(),
                Selected = selectAll
            };
        }
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, object selectedValue)
    {
        return enumerable.ToSelectList(value, text, new List<object>() { selectedValue });
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, IEnumerable<object> selectedValues)
    {
        var sel = selectedValues != null
            ? selectedValues.Where(x => x != null).ToList().ConvertAll<string>(x => x.ToString())
            : new List<string>();

        foreach (var f in enumerable.Where(x => x != null))
        {
            yield return new SelectListItem()
            {
                Value = value(f).ToString(),
                Text = text(f).ToString(),
                Selected = sel.Contains(value(f).ToString())
            };
        }
    }
}

В вашем контроллере вы можете сделать следующее:

var pageOptions = new[] { "10", "15", "25", "50", "100", "1000" };
ViewBag.PageOptions = pageOptions.ToSelectList(o => o, "15" /*selectedValue*/);

И, наконец, в вашем View, поставьте:

@Html.DropDownList("PageOptionsDropDown", ViewBag.PageOptions as IEnumerable<SelectListItem>, "(Select one)")

Это приведет к желаемому результату - конечно, вы можете опустить "(Select one)" optionLabel выше, если вы не хотите первый пустой элемент:

<select id="PageOptionsDropDown" name="PageOptionsDropDown">
<option value="">(Select one)</option>
<option value="10">10</option>
<option selected="selected" value="15">15</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="1000">1000</option>
</select>

Обновление: Пересмотренный список кодов можно найти здесь с комментариями XML.

13 голосов
/ 06 августа 2009

Проблема в том, что SelectList работает как задумано. Баг в дизайне. Вы можете установить Selected Property в SelectedItem, но это будет полностью проигнорировано, если вы просматриваете список с помощью GetEnumerator () (или если Mvc делает это за вас). Вместо этого Mvc создаст новые элементы SelectListItems.

Вы должны использовать ctor SelectList с SelectListItem [], Text-Name, Value-Name и SelectedValue. Будьте внимательны, чтобы передать в качестве SelectedValue ЗНАЧЕНИЕ SelectListItem, которое вы хотите выбрать, а не сам SelectListItem! Пример:

SelectList sl = new SelectList( new[]{
  new SelectListItem{ Text="one", Value="1"},
  new SelectListItem{ Text="two", Value="2"},
  new SelectListItem{ Text="three", Value="3"}
}, "Text", "Value", "2" );

(не проверял это, но у меня была такая же проблема)

тогда вторая опция получит атрибут selected = "selected". Это похоже на старые добрые DataSets; -)

11 голосов
/ 24 июня 2009

Это опция:

myViewData.PageOptionsDropDown = new[] 
{
 new SelectListItem { Text = "10", Value = "10" },
 new SelectListItem { Text = "15", Value = "15", Selected = true }
 new SelectListItem { Text = "25", Value = "25" },
 new SelectListItem { Text = "50", Value = "50" },
 new SelectListItem { Text = "100", Value = "100" },
 new SelectListItem { Text = "1000", Value = "1000" },
}
10 голосов
/ 15 января 2011

Если это буквально все, что вы хотите сделать, то просто объявление массива как строки решает проблему выбранного элемента:

myViewData.PageOptionsDropDown = 
   new SelectList(new string[] {"10", "15", "25", "50", "100", "1000"}, "15");
7 голосов
/ 29 июля 2012

Очень просто совместить SelectList и SelectedValue, даже если ваше свойство не является простым объектом, таким как значение типа Int, String или Double.

Пример:

Предполагается, что наш объект Region выглядит примерно так:

public class Region {
     public Guid ID { get; set; }
     public Guid Name { get; set; }
}

А ваша модель вида выглядит примерно так:

public class ContactViewModel {
     public DateTime Date { get; set; }
     public Region Region { get; set; }
     public List<Region> Regions { get; set; }
}

У вас может быть код ниже:

@Html.DropDownListFor(x => x.Region, new SelectList(Model.Regions, "ID", "Name")) 

Только если вы переопределите метод ToString объекта Region на что-то вроде:

public class Region {
     public Guid ID { get; set; }
     public Guid Name { get; set; }

     public override string ToString()
     {
         return ID.ToString();
     }
}

Это 100% гарантия работы.

Но я действительно считаю, что лучший способ добиться 100% работы SelectList во всех случаях - это использовать метод Equals для проверки значения свойства DropDownList или ListBox для каждого элемента в коллекции элементов.

5 голосов
/ 28 июля 2009

Кажется, что если у вас строго типизированное представление, вам нужно изменить идентификатор раскрывающегося списка, чтобы оно НЕ было именем свойства унаследованного класса. Затем вам нужно добавить некоторую логику в метод редактирования (POST), чтобы извлечь выбранное значение из коллекции FORMCollection и поместить его в свой экземпляр перед принятием изменений.

Это, конечно, немного странно, но я попробовал, и это работает.

Так что, если в вашем классе есть поле с названием CountryId, скажем, и вы отображаете список названий стран, сделайте так, чтобы в раскрывающемся списке был идентификатор CountryName, а не CountryId, тогда в сообщении вы можете сделать что-то с помощью Collection [" CountryName "].

4 голосов
/ 05 сентября 2011

У меня была точно такая же проблема. Решение простое. Просто измените параметр «name», передаваемый помощнику DropDownList, на что-то, что не соответствует ни одному из свойств, существующих в вашей ViewModel. читайте больше здесь: http://www.dotnetguy.co.uk/post/2009/06/25/net-mvc-selectlists-selected-value-does-not-get-set-in-the-view

Я цитирую Дэна Уотсона:

В MVC, если представление строго типизировано, выбранный параметр списка выбора будет переопределен, а свойство выбранного параметра, установленное в конструкторе, никогда не достигнет представления, и вместо этого будет выбран первый параметр в раскрывающемся списке (почему все еще немного загадка).

ура!

...