ASP.NET MVC Html.DropDownList SelectedValue

103

Probé que este es RC1 y luego actualicé a RC2, que no resolvió el problema.

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

resultado: la propiedad SelectedValue se establece en el objeto

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

resultado: todas las opciones esperadas se muestran al cliente, pero el atributo seleccionado no se establece. El elemento de SelectedValue existe dentro de la lista, pero el primer elemento de la lista siempre está predeterminado como seleccionado.

¿Cómo debería hacer esto?

Actualización Gracias a la respuesta de John Feminella descubrí cuál es el problema. "UserId" es una propiedad en el modelo en el que se escribe fuertemente mi vista. Cuando Html.DropDownList ("UserId" se cambia a cualquier otro nombre que no sea "UserId", el valor seleccionado se representa correctamente.

Sin embargo, esto da como resultado que el valor no esté vinculado al modelo.

blu
fuente
¿Está seguro de que el valor está en la lista?
John Sheehan
El problema aún existe en la versión 1 de ASP.NET MVC
Mathias F
¿Qué es selectedUserId !?
Fulvio
¿Cómo se supone que debe vincular el valor en la actualización si el nombre de su entrada no es el mismo que el de su propiedad?
ryudice

Respuestas:

68

Así es como solucioné este problema:

Tuve lo siguiente:

Controlador:

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

Ver

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

Cambiado por lo siguiente:

Ver

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

Parece que el DropDown no debe tener el mismo nombre que el nombre ViewData: S extraño pero funcionó.

Sanchitos
fuente
19
+1 para "DropDown no debe tener el mismo nombre que el nombre ViewData" Muchas gracias
Daniel Dyson
¡¡¡Gracias!!! Acabas de resolver mi problema después de muchas horas de preguntarte por qué esto no funcionó :)
Jen
1
Esto funciona para el valor seleccionado, pero surge un problema cuando intenta actualizar "DealerTypes" en la base de datos, ya que DropDownList ahora está vinculado a "DealerTypesDD" que no existe en el modelo. Una solución es agregar un campo oculto y htmlAttributes a DropDownList: <input type = "hidden" name = "DealerTypes" id = "DealerTypes" value = "" /> <% = Html.DropDownList ("DealerTypesDD", ViewData [ "DealerTypes"] como SelectList, nuevo {@onchange = "DealerTypes.value = this.value"})%>
Zim
3
Es mejor agregar 'DS' al nombre de viewdata, por lo que no arruinará el enlace del modelo ...
noocyte
1
¡Que demonios! Esto no está arreglado en MVC5 ??? Como otro truco mencionado por @Paul Hatcher, copie la identificación seleccionada en ViewData ["DealerTypes"].
jsgoupil
51

Prueba esto:

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

Y entonces:

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

Con MVC RC2, obtengo:

<select id="PeopleClass" name="PeopleClass">
    <option value="1">Name1</option>
    <option selected="selected" value="2">Name2</option>
    <option value="3">Name3</option>
</select>
John Feminella
fuente
Esto fue útil para rastrear la causa. He actualizado mi pregunta para reflejar las adiciones.
blu
es el primero en el modelo? segundo en controlador? y tercero a la vista claro, pero 2 primeros .. ???
rr
Buena respuesta, pero ¿no es mejor usar un ViewModel?
Jess
@Jess: Escribí esta respuesta en marzo de 2009, hace más de 5 años. ¡Los tiempos han cambiado! No dudes en actualizar. :)
John Feminella
5

Aún puede nombrar el DropDown como "UserId" y aún hacer que el enlace del modelo funcione correctamente para usted.

El único requisito para que esto funcione es que la clave ViewData que contiene SelectList no tenga el mismo nombre que la propiedad Model que desea vincular. En su caso específico, esto sería:

// 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"])%>

Esto producirá un elemento de selección que se llama UserId, que tiene el mismo nombre que la propiedad UserId en su modelo y, por lo tanto, el enlazador de modelos lo establecerá con el valor seleccionado en el elemento de selección de html generado por el ayudante Html.DropDownList.

No estoy seguro de por qué ese constructor Html.DropDownList en particular no seleccionará el valor especificado en SelectList cuando pones la lista de selección en ViewData con una clave igual al nombre de la propiedad. Sospecho que tiene algo que ver con la forma en que se usa el asistente DropDownList en otros escenarios, donde la convención es que tiene una SelectList en ViewData con el mismo nombre que la propiedad en su modelo. Esto funcionará correctamente:

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

// in my view
<%=Html.DropDownList("UserId")%>
Rui
fuente
Muchas gracias amigo, sobre todo las respuestas, esto funcionó para mí, no sé por qué, pero lo hizo
Lamin Sanneh
¡Finalmente! Después de horas de intentarlo, finalmente encontré su "único requisito". Eso lo hizo.
SteveCav
2

Si no creemos que este sea un error que el equipo debería corregir, al menos MSDN debería mejorar el documento. La confusión proviene realmente del mal documento de esto. En MSDN , explica el nombre de los parámetros como,

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

Esto solo significa que el html final que genera usará ese parámetro como el nombre de la entrada seleccionada. Pero, en realidad, significa más que eso.

Supongo que el diseñador asume que el usuario usará un modelo de vista para mostrar la lista desplegable, y también usará la publicación de regreso al mismo modelo de vista. Pero en muchos casos, realmente no seguimos esa suposición.

Utilice el ejemplo anterior,

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

Si seguimos la suposición, deberíamos definir un modelo de vista para esta vista relacionada con la lista desplegable

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

Porque cuando se publica, solo se publicará el valor seleccionado, por lo que se asume que se debe publicar en la propiedad del modelo SelectedPersonId, lo que significa que el primer nombre de parámetro de Html.DropDownList debe ser 'SelectedPersonId'. Por lo tanto, el diseñador piensa que cuando se muestra la vista del modelo en la vista, la propiedad del modelo SelectedPersonId debería contener el valor predeterminado de esa lista desplegable. Incluso aunque su Lista <SelectListItem> Personas ya haya configurado el indicador Seleccionado para indicar cuál está seleccionado / predeterminado, tml.DropDownList lo ignorará y reconstruirá su propio IEnumerable <SelectListItem> y establecerá el elemento predeterminado / seleccionado según el nombre.

Aquí está el código de 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);
}

Entonces, el código en realidad fue más allá, no solo intenta buscar el nombre en el modelo, sino también en los datos de vista, tan pronto como encuentre uno, reconstruirá la lista de selección e ignorará su Seleccionado original.

El problema es que, en muchos casos, realmente no lo usamos de esa manera. solo queremos incluir una lista de selección con uno o varios elementos seleccionados como verdadero.

Por supuesto, la solución es simple, use un nombre que no esté en el modelo ni en los datos de vista. Cuando no puede encontrar una coincidencia, utilizará la lista de selección original y la selección original tendrá efecto.

Pero sigo pensando que MVC debería mejorarlo agregando una condición más

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

Porque, si la selectList original ya ha tenido una seleccionada, ¿por qué ignoraría eso?

Solo mis pensamientos.

liuhongbo
fuente
Esta es realmente la mejor explicación que he leído hasta ahora. Me ahorró mucho dolor de cabeza. Todo lo que tenía que hacer era establecer el valor de la propiedad viewmodel y funcionó. Gracias.
Moisés Machua
2

El código de la publicación anterior de MVC 3 no funciona, pero es un buen comienzo. Yo lo arreglare. Probé este código y funciona en MVC 3 Razor C # Este código usa el patrón ViewModel para completar una propiedad que devuelve un List<SelectListItem>.

La clase modelo

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

La clase ViewModel

using System.Web.Mvc;

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

El método del controlador

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

La vista

@model MvcApp.ViewModels.ProductListViewModel

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

La salida HTML será algo como

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

dependiendo de cómo formatee la salida. Espero que esto ayude. El código funciona.

wayne.blackmon
fuente
1
Esto no establece la selectedopción.
Jess
Para establecer el elemento seleccionado mediante SelectListItem, establezca la propiedad Selected en true.
wayne.blackmon
1

Esto parece ser un error en la clase SelectExtensions ya que solo verificará ViewData en lugar del modelo para el elemento seleccionado. Entonces, el truco consiste en copiar el elemento seleccionado del modelo en la colección ViewData con el nombre de la propiedad.

Esto se toma de la respuesta que di en los foros de MVC, también tengo una respuesta más completa en una publicación de blog que usa el atributo DropDownList de Kazi ...

Dado un modelo

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

y un modelo de vista básico de

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

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

Luego escribimos una plantilla de editor DropDownList de la siguiente manera ...

<%@ 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()) %>

Esto también funcionará si cambia ArticleType en el modelo de vista a un SelectListItem, aunque tiene que implementar un convertidor de tipo según el blog de Kazi y registrarlo para forzar al vinculador a tratar esto como un tipo simple.

En su controlador tenemos entonces ...

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);
     }
     ...
}
Paul Hatcher
fuente
Gracias por la respuesta, tendré que revisar esto.
Blu
0

El problema es que los buzones no funcionan igual que los cuadros de lista, al menos de la forma que espera el diseño de ASP.NET MVC2: un cuadro desplegable solo permite cero o un valor, ya que los cuadros de lista pueden tener una selección de valores múltiples. Entonces, siendo estricto con HTML, ese valor no debería estar en la lista de opciones como bandera "seleccionada", sino en la entrada misma.

Vea el siguiente ejemplo:

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

El combo tiene la opción seleccionada, pero también tiene establecido su atributo de valor . Por lo tanto, si desea que ASP.NET MVC2 represente un cuadro desplegable y también tenga un valor específico seleccionado (es decir, valores predeterminados, etc.), debe darle un valor en la representación, como este:

// in my view             
<%=Html.DropDownList("UserId", selectListItems /* (SelectList)ViewData["UserId"]*/, new { @Value = selectedUser.Id } /* Your selected value as an additional HTML attribute */)%>
dalotodo
fuente
0

En ASP.NET MVC 3, simplemente puede agregar su lista a 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;

... y luego hacer referencia a él por su nombre en la vista de la maquinilla de afeitar ...

@Html.DropDownList("options")

No tiene que "usar" manualmente la lista en la llamada DropDownList. Al hacerlo de esta manera, también establecí correctamente el valor seleccionado para mí.

Descargo de responsabilidad:

  1. No he probado esto con el motor de visualización de formularios web, pero también debería funcionar.
  2. No lo he probado en v1 y v2, pero podría funcionar.
John Bubriski
fuente
0

Logré obtener el resultado deseado, pero con un enfoque ligeramente diferente. En la lista desplegable utilicé el modelo y luego lo hice referencia. No estoy seguro de si esto era lo que buscaba.

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

Model.Matters.FeeStructures en arriba es mi identificación, que podría ser su valor del artículo que debe seleccionarse.

Jukes
fuente