ASP.NET MVC Html.DropDownList SelectedValue


103

Ho provato questo è RC1 e poi aggiornato a RC2 che non ha risolto il problema.

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

risultato: la proprietà SelectedValue è impostata sull'oggetto

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

risultato: tutte le opzioni previste vengono rese al client, ma l'attributo selezionato non è impostato. L'elemento in SelectedValue esiste nell'elenco, ma il primo elemento nell'elenco è sempre selezionato per impostazione predefinita.

Come dovrei farlo?

Aggiornamento Grazie alla risposta di John Feminella ho scoperto qual è il problema. "UserId" è una proprietà nel Modello su cui è fortemente digitato la mia vista. Quando Html.DropDownList ("UserId" viene modificato in qualsiasi altro nome tranne "UserId", il valore selezionato viene visualizzato correttamente.

Ciò fa sì che il valore non sia legato al modello.


Sei sicuro che il valore sia nella lista?
John Sheehan

Il problema esiste ancora nella versione 1 di ASP.NET MVC
Mathias F

Cosa è selectedUserId !?
fulvio

Come pensi di associare il valore all'aggiornamento se il nome del tuo input non è lo stesso della tua proprietà?
ryudice

Risposte:


68

Ecco come ho risolto questo problema:

Ho avuto quanto segue:

controller:

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

Visualizza

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

Modificato da quanto segue:

Visualizza

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

Sembra che il DropDown non debba avere lo stesso nome ha il nome ViewData: S strano ma ha funzionato.


19
+1 per "il DropDown non deve avere lo stesso nome ha il nome ViewData" Grazie mille
Daniel Dyson

Grazie!!! Hai appena risolto il mio problema dopo molte ore in cui mi chiedevo perché non funzionasse :)
Jen

1
Funziona per il valore selezionato, ma si verifica un problema quando si tenta di aggiornare "DealerTypes" nel db poiché DropDownList è ora associato a "DealerTypesDD" che non esiste nel modello. Una soluzione alternativa consiste nell'aggiungere un campo nascosto e htmlAttributes a DropDownList: <input type = "hidden" name = "DealerTypes" id = "DealerTypes" value = "" /> <% = Html.DropDownList ("DealerTypesDD", ViewData [ "DealerTypes"] come SelectList, nuovo {@onchange = "DealerTypes.value = this.value"})%>
Zim

3
È meglio aggiungere "DS" al nome viewdata, quindi non rovinare l'associazione del modello ...
noocyte

1
Che diavolo! Questo non è stato risolto in MVC5 ??? Come un altro trucco menzionato da @Paul Hatcher, copia l'id selezionato in ViewData ["DealerTypes"].
jsgoupil

51

Prova questo:

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

E poi:

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, ottengo:

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

Questo è stato utile per rintracciare la causa. Ho aggiornato la mia domanda per riflettere le aggiunte.
blu

è il primo nel modello? secondo nel controller? e terzo in vista è chiaro, ma 2 primi .. ???
rr

Buona risposta, ma non è meglio usare un ViewModel?
Jess

@Jess: ho scritto questa risposta nel marzo 2009, più di 5 anni fa. I tempi sono cambiati! Sentiti libero di aggiornare. :)
John Feminella

5

È ancora possibile denominare il DropDown come "UserId" e l'associazione del modello funziona ancora correttamente.

L'unico requisito affinché funzioni è che la chiave ViewData che contiene il SelectList non abbia lo stesso nome della proprietà Model che si desidera associare. Nel tuo caso specifico questo sarebbe:

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

Questo produrrà un elemento di selezione denominato UserId, che ha lo stesso nome della proprietà UserId nel modello e quindi il raccoglitore di modelli lo imposterà con il valore selezionato nell'elemento di selezione dell'html generato dall'helper Html.DropDownList.

Non sono sicuro del motivo per cui quel particolare costruttore Html.DropDownList non selezionerà il valore specificato in SelectList quando metti l'elenco di selezione in ViewData con una chiave uguale al nome della proprietà. Sospetto che abbia qualcosa a che fare con il modo in cui l'helper DropDownList viene utilizzato in altri scenari, in cui la convenzione è che hai un SelectList in ViewData con lo stesso nome della proprietà nel tuo modello. Funzionerà correttamente:

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

// in my view
<%=Html.DropDownList("UserId")%>

Grazie mille amico, soprattutto le risposte ha funzionato per me, non so perché ma ha funzionato
Lamin Sanneh

Finalmente! Dopo ore di tentativi ho finalmente trovato il tuo "unico requisito". Quello ha fatto.
SteveCav

2

Se pensiamo che questo non sia un bug che il team dovrebbe correggere, MSDN dovrebbe migliorare il documento. La confusione viene davvero dal povero documento di questo. In MSDN , spiega il nome dei parametri come,

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

Questo significa solo che l'html finale che genera utilizzerà quel parametro come nome dell'ingresso di selezione. Ma in realtà significa molto di più.

Immagino che il progettista presuma che l'utente utilizzerà un modello di visualizzazione per visualizzare l'elenco a discesa, inoltre utilizzerà il post nello stesso modello di visualizzazione. Ma in molti casi, non seguiamo davvero questo assunto.

Usa l'esempio sopra,

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

Se seguiamo il presupposto, dovremmo definire un modello di visualizzazione per questa vista correlata all'elenco a discesa

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

Perché quando torna dopo, solo il valore selezionato post indietro, in modo da assumere dovrebbe inviare di nuovo alla proprietà SelectedPersonId del modello, il che significa che il primo parametro di Html.DropDownList nome dovrebbe essere 'SelectedPersonId'. Pertanto, il progettista pensa che quando si visualizza la vista del modello nella vista, la proprietà SelectedPersonId del modello dovrebbe contenere il valore predefinito di tale elenco a discesa. Anche se List <SelectListItem> Persons ha già impostato il flag Selected per indicare quale è selezionato / predefinito, tml.DropDownList lo ignorerà effettivamente e ricostruirà il proprio IEnumerable <SelectListItem> e imposterà l'elemento predefinito / selezionato in base al nome.

Ecco il codice da 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);
}

Quindi, il codice in realtà è andato oltre, non solo prova a cercare il nome nel modello, ma anche nei dati di visualizzazione, non appena ne trova uno, ricostruirà la selectList e ignorerà il Selected originale.

Il problema è che, in molti casi, non lo usiamo davvero in questo modo. vogliamo solo inserire una selectList con uno / più elementi selezionati impostati su true.

Ovviamente la soluzione è semplice, usa un nome che non sia nel modello né nel viewdata. Quando non riesce a trovare una corrispondenza, utilizzerà la selectList originale e la Selected originale avrà effetto.

Ma penso ancora che mvc dovrebbe migliorarlo aggiungendo un'altra condizione

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

Perché, se la selectList originale ne ha già una selezionata, perché ignorarla?

Solo i miei pensieri.


Questa è davvero la migliore spiegazione che ho letto finora. Mi ha risparmiato un sacco di mal di testa. Tutto quello che dovevo fare era impostare il valore della proprietà viewmodel e ha funzionato. Grazie.
Moses Machua

2

Il codice nel precedente post MVC 3 non funziona ma è un buon inizio. Io lo aggiusterò. Ho testato questo codice e funziona in MVC 3 Razor C # Questo codice utilizza il pattern ViewModel per popolare una proprietà che restituisce un file List<SelectListItem>.

La classe Model

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

La classe ViewModel

using System.Web.Mvc;

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

Il metodo del 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)
}

L'output HTML sarà qualcosa di simile

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

a seconda di come formatti l'output. Spero che aiuti. Il codice funziona.


1
Questo non imposta l' selectedopzione.
Jess

Per impostare l'elemento selezionato utilizzando SelectListItem, impostare la proprietà Selected su true.
wayne.blackmon

1

Questo sembra essere un bug nella classe SelectExtensions in quanto controllerà solo ViewData anziché il modello per l'elemento selezionato. Quindi il trucco è copiare l'elemento selezionato dal modello nella raccolta ViewData sotto il nome della proprietà.

Questo è tratto dalla risposta che ho dato sui forum MVC, ho anche una risposta più completa in un post sul blog che utilizza l'attributo DropDownList di Kazi ...

Dato un modello

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

e un modello di visualizzazione di base di

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

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

Quindi scriviamo un modello di editor DropDownList come segue ..

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

Funzionerà anche se si modifica ArticleType nel modello di visualizzazione in un SelectListItem, sebbene sia necessario implementare un convertitore di tipi come da blog di Kazi e registrarlo per forzare il raccoglitore a trattarlo come un tipo semplice.

Nel tuo controller abbiamo quindi ...

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

Grazie per la risposta, dovrò controllare questo.
blu

0

Il problema è che le caselle personali non funzionano come le caselle di riepilogo, almeno nel modo previsto dalla progettazione di ASP.NET MVC2: una casella personale consente solo zero o uno valori, poiché le caselle di riepilogo possono avere una selezione di più valori. Quindi, essendo rigoroso con l'HTML, quel valore non dovrebbe essere nell'elenco delle opzioni come flag "selezionato", ma nell'input stesso.

Vedi il seguente esempio:

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

La combo ha l'opzione selezionata, ma ha anche il suo valore di attributo impostato . Quindi, se vuoi che ASP.NET MVC2 esegua il rendering di una casella personale e abbia anche un valore specifico selezionato (cioè valori predefiniti, ecc.), Dovresti assegnargli un valore nel rendering, come questo:

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

0

In ASP.NET MVC 3 puoi semplicemente aggiungere il tuo elenco 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;

... e poi referenziarlo per nome nella tua vista rasoio ...

@Html.DropDownList("options")

Non è necessario "utilizzare" manualmente l'elenco nella chiamata DropDownList. In questo modo imposta correttamente il valore selezionato anche per me.

Disclaimer:

  1. Non l'ho provato con il motore di visualizzazione dei moduli Web, ma dovrebbe funzionare anche.
  2. Non l'ho testato in v1 e v2, ma potrebbe funzionare.

0

Sono riuscito a ottenere il risultato desiderato, ma con un approccio leggermente diverso. Nell'elenco a discesa ho usato il modello e poi l'ho fatto riferimento. Non sono sicuro che fosse quello che stavi cercando.

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

Model.Matters.FeeStructures sopra è il mio id, che potrebbe essere il tuo valore dell'articolo che dovrebbe essere selezionato.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.