¿Cómo puedo hacer que esta ASP.NET MVC SelectList funcione?

126

Creo una selectList en mi controlador, para mostrar en la vista.

Estoy tratando de crearlo sobre la marcha, algo así ... así ...

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

Se compila, pero el resultado es malo ...

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

¿Observa cómo no se selecciona ningún elemento?

¿¿Cómo puedo arreglar esto??

Pure.Krome
fuente
1
Seis respuestas ... una favorita ... sin votos a favor: / Le daré un +1
womp

Respuestas:

128

Así es como lo hago

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

A segunda vista, no estoy seguro de saber lo que buscas ...

mhenrixon
fuente
2
Gracias, esto me ayudó a resolver esto.
Cadoo
66
¡No soluciona el problema del elemento seleccionado, pero es una excelente manera de evitar cadenas mágicas para listas seleccionadas! Salud
Alan Christensen
11
Para solucionar el problema elemento seleccionado, añada el siguiente código: SelectList sList = new SelectList(selectList, "Text", "Value", selected);donde selectedestá la corriente seleccionados por el cliente
jao-
68

Yo uso un método de extensión:

uso

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

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

con

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;
    }
}
Thomas Stock
fuente
bonito. He agregado un Func seleccionado, una opción DefaultValue y una segunda sobrecarga sin ningún valor predeterminado para especificar y esto funciona de maravilla. Por cierto, si desea que el tipo de retorno IEnumerable <SelectListItem> pueda usar la sintaxis de retorno de rendimiento para que esto se vea realmente limpio
Chris Meek
48

Usando el constructor que acepta items, dataValueField, dataTextField, selectedValuecomo parámetros:

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

Entonces en tu opinión:

<%=Html.DropDownList("myList") %>
Çağdaş Tekin
fuente
3
buen amigo :) me gusta! estaba tratando de hacer esto ... pero no pude obtener la parte .Seleccionar (...) ... agradable :) Lo probaré en casa, más tarde :)
Pure.Krome
1
Heya Cagdas funciona muy bien, excepto que el campo seleccionado no está seleccionado. ¿alguna idea? ¿Es esto un error en las cosas de MVC?
Pure.Krome
2
El elemento seleccionado funciona cuando agrega el elemento SelectList al objeto ViewData (como en ViewData ["myList"] = new SelectList ..) y luego lo representa con <% = Html.DropDownList ("myList")%>. Sin embargo, no pude hacer que el elemento seleccionado funcione sin hacerlo de esta manera. Extraño.
Çağdaş Tekin
@Cagdas - hola de nuevo amigo ... todavía no lo he resuelto :( ¿Qué es esto de un ViewData? Tengo un ViewData fuertemente tipado con su propia propiedad ... ???
Pure.Krome
@ Pure.Krome: tomé esa propiedad de su ejemplo en la pregunta. Ahora estoy editando el ejemplo de código. Por favor, vea mi edición en unos segundos.
Çağdaş Tekin el
19

Partiendo de la respuesta de Thomas Stock, creé estos ToSelectListmétodos sobrecargados :

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

En su controlador, puede hacer lo siguiente:

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

Y finalmente en su Vista, ponga:

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

"(Select one)"Producirá el resultado deseado; por supuesto, puede omitir la opción LabelLabel anterior si no desea el primer elemento vacío:

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

Actualización: Una lista de código revisada se puede encontrar aquí con comentarios XML.

JustinStolle
fuente
13

El problema es que SelectList funciona según lo diseñado. El error está en el diseño. Puede establecer la Propiedad seleccionada en SelectedItem, pero esto se ignorará por completo si recorre la lista con GetEnumerator () (o si Mvc lo hace por usted). Mvc creará nuevos SelectListItems en su lugar.

Debe usar el ctor SelectList con SelectListItem [], Text-Name, Value-Name y SelectedValue. ¡Tenga en cuenta pasar como SelectedValue el VALOR de SelectListItem, que desea seleccionar, no el SelectListItem en sí! Ejemplo:

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" );

(No probé esto, pero tuve el mismo problema)

entonces la segunda opción obtendrá el atributo selected = "selected". Eso parece buenos DataSets viejos ;-)

Codificación ido
fuente
11

Esta es una opción:

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" },
}
murki
fuente
nuevo SelectListItem {Selected = true, Text = "15", Value = "15"} para el valor seleccionado.
Cadoo
oops, sí, olvidé incluir la propiedad 'Seleccionada', gracias Cadoo =)
murki
10

Si eso es literalmente todo lo que quiere hacer, simplemente declarar la matriz como cadena corrige el problema del elemento seleccionado:

myViewData.PageOptionsDropDown = 
   new SelectList(new string[] {"10", "15", "25", "50", "100", "1000"}, "15");
Algodón mate
fuente
7

Es muy simple hacer que SelectList y SelectedValue trabajen juntos, incluso si su propiedad no es un objeto simple como Int, String o Double value.

Ejemplo:

Asumiendo que nuestro objeto Región es algo como esto:

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

Y su modelo de vista es algo así como:

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

Puedes tener el siguiente código:

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

Solo si anula el método ToString del objeto Región a algo como:

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

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

Esto tiene 100% de garantía para trabajar.

Pero realmente creo que la mejor manera de hacer que SelectList funcione al 100% en todas las circunstancias es usar el método Equals para probar el valor de la propiedad DropDownList o ListBox en cada elemento de la colección de elementos.

ararog
fuente
5

Parece que si tiene una vista fuertemente tipada necesita cambiar la ID del menú desplegable para que NO sea el nombre de una propiedad en la clase heredada. Luego debe poner algo de lógica en su método de edición (POST) para obtener el valor seleccionado de FORMCollection y ponerlo en su instancia antes de confirmar sus cambios.

Esto es ciertamente un poco extraño, pero lo probé y funciona.

Entonces, si su clase tiene un campo llamado CountryId, y está mostrando una lista de nombres de países, haga que el menú desplegable tenga una identificación de CountryName en lugar de CountryId, luego en la publicación, puede hacer algo con Collection ["CountryName"] .


fuente
1
Sí, tuve este mismo problema. Simplemente cambiando el nombre funcionó. ¡Gracias!
davekaro
4

Tuve exactamente el mismo problema. La solución es simple. Simplemente cambie el parámetro "nombre" pasado al asistente de DropDownList a algo que no coincida con ninguna de las propiedades existentes en su ViewModel. Lea más aquí: http://www.dotnetguy.co.uk/post/2009/06/25/net-mvc-selectlists-selected-value-does-not-get-set-in-the-view

Cito al Dan Watson:

En MVC, si la vista está fuertemente tipada, la opción seleccionada de la lista de selección se anulará y la propiedad de opción seleccionada establecida en el constructor nunca alcanzará la vista y la primera opción en el menú desplegable se seleccionará en su lugar (¿por qué sigue siendo un misterio? .

¡salud!

cleftheris
fuente
3

Todas estas respuestas se ven geniales, pero parece ser que el controlador está preparando datos para una Vista en un formato estructurado conocido en lugar de permitir que la vista simplemente repita un IEnumerable <> entregado a través del modelo y cree una lista de selección estándar y luego permita que DefaultModelBinder entregarle el artículo seleccionado a través de un parámetro de acción. Si, no, porque no? Separación de preocupaciones, ¿sí? Parece extraño tener el controlador para construir algo tan específico para la interfaz de usuario y la vista.


fuente
3

Sencillo:

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

Simplemente lo ejecuté así y no tuve problemas,

public class myViewDataObj
    {
        public SelectList PageOptionsDropDown { get; set; }
    }

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

            ViewData["myList"] = myViewData.PageOptionsDropDown;
            return View();
        }

y

<%=Html.DropDownList("myList") %>

También funcionó si haces esto,

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

            ViewData["myListValues"] = myViewData.PageOptionsDropDown;
            ViewData["myList"] = "15";
            return View();
        }

y

<%=Html.DropDownList("myList",(IEnumerable<SelectListItem>)ViewData["myListValues"]) %>
Tony Borf
fuente
1

Usando su ejemplo, esto funcionó para mí:

controlador:

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

ver:

<%= Html.DropDownList("PageOptionsDropDown")%>
Cadoo
fuente
Sí, eso también funciona para mí. En mi caso, la lista es un objeto Modelo de entidad. Controlador: ViewData ["Categorías"] = nueva SelectList (db.Categories.ToList (), "CategoryId", "CategoryName", "15"); Ver: Html.DropDownList ("CategoryId", (SelectList) ViewData ["Categorías"], "--select--")
Junior Mayhé
1
MonthRepository monthRepository = new MonthRepository();
IQueryable<MonthEntity> entities = monthRepository.GetAllMonth();
List<MonthEntity> monthEntities = new List<MonthEntity>();

foreach(var r in entities)
{
    monthEntities.Add(r);
}

ViewData["Month"] = new SelectList(monthEntities, "MonthID", "Month", "Mars");
Cactus
fuente
1

Lo hago así:

List<SelectListItem> list = new List<SelectListItem>{
new SelectListItem {Selected = true, Text = "Select", Value = "0"},
new SelectListItem {Selected = true, Text = "1", Value = "1"},
new SelectListItem {Selected = true, Text = "2", Value = "2"}
};
return list.ToArray();

ToArray () se encarga de los problemas.

femseks
fuente
1

Si desea pasar un texto aleatorio a su DropDownList, por ejemplo --Seleccionar-- , puede hacerlo fácilmente con este código:

@Html.DropDownListFor(x => x.CategoryId, new SelectList(Model.Categories, "Id", "Name"), "--Select--", new { @class = "form-control" })
Munich
fuente
0

Puede darse el caso de que tenga cierta ambigüedad en su ViewData:

Echa un vistazo aquí

KP.
fuente
0

el valor seleccionado en el modelo aprovecha en lugar del elemento predeterminado. (Estoy de acuerdo en que no leí todas las publicaciones)

Daniel
fuente
0

No recuerdo cómo se configuró mvc 1, pero parece que también quería que la lista de selección tuviera el mismo nombre que el campo al que pertenecía ...

Lo que encontré, como alguien dijo anteriormente, es que mis listas de selección no funcionaban en mvc2 cuando ViewData se enviaron como se llamaba igual que el campo.

Por ejemplo:

<%= Html.DropDownListFor((model => model.ForID), (SelectList)ViewData["ForName"]) %>

funciona cuando

<%= Html.DropDownListFor((model => model.ForID), (SelectList)ViewData["ForID"]) %>

no funciona ya que el nombre de ViewData "ForID" se llama igual que el campo para el que está trabajando

marca
fuente
0

Una posible explicación es que el valor de la lista de selección al que está vinculando no es una cadena.

Entonces, en ese ejemplo, ¿el parámetro 'PageOptionsDropDown' es una cadena en su modelo? Porque si no es así, el valor seleccionado en la lista no se mostrará.

FruitDealer
fuente
0

Si busca en el código fuente MVC 2 en el método de extensión Html.DropDownList, nunca verifica la propiedad SelectValue de la clase SelectList. Solo intentará coincidir contra su Modelo.

Todo lo anterior son variaciones de un tema, es decir, ¿cómo se envían un montón de datos a la vista para obtener una lista desplegable y todos son tan buenos entre sí (más o menos).

El problema está en la vista. Cree su propio método de extensión DropDownList que respete el valor de selección que establezca o repita a mano. Lo que funcione mejor para ti.

Simon Halsey
fuente
@Franz Antesberger dice lo mismo.
Simon Halsey
0

Si tiene una colección en su modelo y su Vista es de tipo fuerte, alguna variación de esto funcionará:

@Html.DropDownListFor(x => x.RegionID, 
    new SelectList(Model.Regions,"RegionID", "RegionName", Model.RegionID))

-o-

@Html.DropDownList("RegionID", 
    new SelectList(Model.Regions, "RegionID", "RegionName", Model.RegionID))
marca
fuente