2009-03-09 24 views
96

He intentado esto RC1 y luego actualicé a RC2 que no resolvió el problema.ASP.NET MVC Html.DropDownList SelectedValue

// 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 que se prevé se prestan al cliente, pero el atributo seleccionado no está establecido. El elemento en SelectedValue existe dentro de la lista, pero el primer elemento de la lista siempre está predeterminado en selected.

¿Cómo debería estar haciendo esto?

actualización Gracias a la respuesta de John Feminella descubrí cuál es el problema. "UserId" es una propiedad del Modelo en el que mi View está fuertemente tipada. Cuando se cambia Html.DropDownList ("identificación de usuario" a cualquier otro nombre, pero "identificación de usuario", el valor seleccionado se procesa de forma correcta

Esto se traduce en el valor Sin estar ligado al modelo embargo

+0

¿Está seguro de que el valor está en la lista? –

+0

sí, está en la lista – blu

+0

¿Qué es selectedUserId? – fuzz

Respuesta

48

probar esto..:

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

Y luego:

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, me sale:

<select id="PeopleClass" name="PeopleClass"> 
    <option value="1">Name1</option> 
    <option selected="selected" value="2">Name2</option> 
    <option value="3">Name3</option> 
</select> 
+0

Esto fue útil para rastrear la causa. He actualizado mi pregunta para reflejar las adiciones. – blu

+0

es el primero en el modelo? segundo en el controlador? y tercero a la vista, claro, pero 2 primeros ... ??? –

+0

Buena respuesta, pero ¿no es mejor usar un ViewModel? – Jess

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 es copiar el elemento seleccionado del modelo en la colección ViewData bajo el nombre de la propiedad.

Esto está tomado de la respuesta que he dado en los foros de MVC, también tengo una respuesta más completa en un blog post que utiliza Kazi's DropDownList attribute ...

dado un modelo de

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 una básica vista del modelo de

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

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

Entonces escribimos un editor de plantillas 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 SelectListItem, aunque tiene que implementar un convertidor de tipo según el blog de Kazi y registrarlo para forzar que el cuaderno trate esto como un tipo simple.

En su controlador tenemos ...

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

Gracias por la respuesta, tendré que verificar esto. – blu

68

Esto es cómo me fijo este problema:

tuve el siguiente:

controlador:

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

Ver

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

cambiado por el siguiente:

Ver

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

Parece que el menú desplegable no debe tener el mismo nombre tiene el nombre ViewData: Es raro pero funcionó.

+19

+1 para "DropDown no debe tener el mismo nombre tiene el nombre ViewData" Gracias grandes –

+0

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 intentas actualizar "DealerTypes" en el db ya que es el DropDownList el que ahora está vinculado a " DealerTypesDD "que no existe en el modelo. Una solución es agregar un campo oculto y htmlAttributes a DropDownList: <% = Html.DropDownList ("DealerTypesDD", ViewData [ "DealerTypes"] como SelectList, nueva {@OnChange = "DealerTypes.value = this.value"})%> – ZimSystem

0

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

Véase 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 su atributo de valor establecido. Por lo tanto, si desea ASP.NET MVC2 para hacer una caja de reenvío y también tienen un valor específico seleccionado (es decir, los valores por defecto, etc.), que deben darle un valor en la prestación, así:

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

en ASP.NET MVC 3 simplemente puede añadir a su lista de 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 ella por su nombre en la vista de afeitar ...

@Html.DropDownList("options") 

usted don' t tiene que "usar" manualmente la lista en DropDownList llamada. Haciéndolo de esta manera, también establezca correctamente el valor seleccionado para mí.

responsabilidad:

  1. no he probado esto con las formas de la tela ver el motor, pero debería funcionar también.
  2. No he probado esto en la v1 y v2, pero podría funcionar.
2

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

la clase del 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 Controller

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 así 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 dar formato a la salida. Espero que esto ayude. El código funciona

+1

Esto no establece la opción 'selected'. – Jess

+0

Para establecer el elemento seleccionado utilizando SelectListItem, establezca la propiedad Selected en verdadero. –

5

Todavía puede nombrar el menú desplegable como "identificación de usuario" y todavía tienen modelo de unión de funcionar correctamente para usted.

El único requisito para que esto funcione es que la clave ViewData que contiene el SelectList no tiene el mismo nombre que la propiedad de modelo que desea enlazar. En su caso específico 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 denomina identificación de usuario, que tiene el mismo nombre que la propiedad UserId en su modelo y, por tanto, el ligante modelo será establecido con el valor seleccionado en el elemento de selección del html generado por el helper Html.DropDownList.

No estoy seguro de por qué ese constructor Html.DropDownList en particular no seleccionará el valor especificado en la lista de selección cuando coloca 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 de DropDownList en otros escenarios, donde la convención es que usted tiene una Lista de selección 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")%> 
+0

Gracias mucho mate, por encima de todas las respuestas Esto funcionó para mí, no sé por qué, pero lo hizo –

+0

Finalmente! Después de horas intentando, finalmente encontré tu "único requisito". Eso lo hizo. – SteveCav

2

Si no creemos que esto es un error que el equipo debe fijar, en el arriendo de MSDN debería mejorar el documento. La confusión realmente proviene del pobre documento de esto. En MSDN, se explican los parámetros nombre como,

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

Esto sólo significa el final de HTML que genera va a utilizar ese parámetro como el nombre de la selección de entrada. Pero, en realidad significa más que eso.

Supongo que el diseñador asume que el usuario utilizará un modelo de vista para mostrar la lista desplegable, también utilizará la publicación de nuevo en el modelo de vista misma. 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 hipótesis, debemos definir un modelo de vista para esta vista relacionados con lista desplegable

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

Porque cuando segundo palo, sólo el valor seleccionado publicar de nuevo, por lo que supone que debe publicar de nuevo en la propiedad del modelo SelectedPersonId, lo que significa que el primer parámetro de Html.DropDownList nombre debe ser 'SelectedPersonId'. Por lo tanto, el diseñador piensa que cuando se muestra la vista del modelo en la vista, la propiedad SelectedPersonId del modelo debe contener el valor predeterminado de esa lista desplegable. Aunque su lista < SelectListItem> Personas ya estableció el indicador seleccionado para indicar cuál se selecciona/predeterminado, el tml.DropDownList ignorará eso 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); 
} 

Por lo tanto, el código en realidad fueron más allá, no sólo se trata de buscar el nombre en el modelo, sino también en el viewdata, tan pronto como sea encuentra uno, reconstruirá la lista de selección e ignorará su selección original.

El problema es que, en muchos casos, realmente no lo utilizamos de esa manera. solo queremos agregar una lista de selección con uno/varios ítems seleccionados seleccionados como verdaderos.

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

pero sigo pensando MVC debe mejorarlo por añadir una condición más

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

Porque, si el original ha selectlist ya había seleccionado, ¿por qué ignorar eso?

Sólo mis pensamientos.

+0

Esta es realmente la mejor explicación que he leído hasta ahora. Me salvó un montón de dolor de cabeza. Todo lo que tenía que hacer era establecer el valor de la propiedad viewmodel y funcionó. Gracias. –

Cuestiones relacionadas