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.