Se não achamos que este é um bug que a equipe deve corrigir, pelo menos o MSDN deve melhorar o documento. O confuso realmente vem do pobre documento disso. No MSDN , ele explica o nome dos parâmetros como,
Type: System.String
The name of the form field to return.
Isso significa apenas que o html final que ele gera usará esse parâmetro como o nome da entrada selecionada. Mas, na verdade, significa mais do que isso.
Eu acho que o designer assume que o usuário usará um modelo de visualização para exibir a lista suspensa, também usará a postagem de volta para o mesmo modelo de visualização. Mas em muitos casos, não seguimos realmente essa suposição.
Use o exemplo acima,
public class Person {
public int Id { get; set; }
public string Name { get; set; }
}
Se seguirmos a suposição, devemos definir um modelo de visualização para esta visualização relacionada à lista suspensa
public class PersonsSelectViewModel{
public string SelectedPersonId,
public List<SelectListItem> Persons;
}
Porque quando postar de volta, apenas o valor selecionado será postado de volta, então ele assume que deve postar de volta na propriedade SelectedPersonId do modelo, o que significa que o primeiro nome de parâmetro de Html.DropDownList deve ser 'SelectedPersonId'. Portanto, o designer pensa que, ao exibir a vista do modelo na vista, a propriedade do modelo SelectedPersonId deve conter o valor padrão dessa lista suspensa. Mesmo que sua Lista de <SelectListItem> Pessoas já tenha definido o sinalizador Selecionado para indicar qual é selecionado / padrão, o tml.DropDownList irá realmente ignorar isso e reconstruir seu próprio IEnumerable <SelectListItem> e definir o item padrão / selecionado com base no nome.
Aqui está o código do 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);
}
Então, o código realmente foi além, ele não apenas tenta pesquisar o nome no modelo, mas também no viewdata, assim que encontrar um, ele irá reconstruir a selectList e ignorar seu Selected original.
O problema é que, em muitos casos, não o usamos realmente dessa forma. queremos apenas adicionar uma selectList com um / vários item (ns) Selected set true.
Claro que a solução é simples, use um nome que não está no modelo nem nos dados de exibição. Quando não consegue encontrar uma correspondência, ele usará a selectList original e o Selected original terá efeito.
Mas ainda acho que o MVC deve melhorá-lo adicionando mais uma condição
if ((defaultValue != null) && (!selectList.Any(i=>i.Selected)))
{
selectList = GetSelectListWithDefaultValue(selectList, defaultValue, allowMultiple);
}
Porque, se a selectList original já teve uma Selecionada, por que você ignoraria isso?
Apenas meus pensamentos.