Como faço para que este ASP.NET MVC SelectList funcione?

126

Eu crio uma selectList no meu controlador para exibir na exibição.

Estou tentando criá-lo em tempo real, meio que uma coisa ... assim ...

myViewData.PageOptionsDropDown = 
   new SelectList(new [] {"10", "15", "25", "50", "100", "1000"}, "15");

Compila, mas a saída é ruim ...

<select id="PageOptionsDropDown" name="PageOptionsDropDown">
    <option>10</option>
    <option>15</option>
    <option>25</option>
    <option>50</option>
    <option>100</option>
    <option>1000</option>
</select>

Observe como nenhum item é selecionado?

Como posso consertar isso??

Pure.Krome
fonte
1
Seis respostas ... uma favorited ... não upvotes: / eu vou dar-lhe um +1
womp

Respostas:

128

É assim que eu faço

IList<Customer> customers = repository.GetAll<Customer>();
IEnumerable<SelectListItem> selectList = 
    from c in customers
    select new SelectListItem
    {
        Selected = (c.CustomerID == invoice.CustomerID),
        Text = c.Name,
        Value = c.CustomerID.ToString()
    };

À segunda vista, não sei ao certo o que você está procurando ...

mhenrixon
fonte
2
Obrigado, isso me ajudou a descobrir essas coisas.
Cadoo
6
Não corrige o problema do item selecionado, mas é uma ótima maneira de evitar seqüências mágicas para listas de seleção! Cheers
Alan Christensen
11
Para corrigir o problema do item selecionado, adicione o seguinte código: SelectList sList = new SelectList(selectList, "Text", "Value", selected);onde selectedestá o cliente selecionado atual
jao 07/07
68

Eu uso um método de extensão:

uso

var departmentItems = departments.ToSelectList(d => d.Code + 
                                               " - " + d.Description,
                                               d => d.Id.ToString(),
                                               " - ");

var functionItems = customerFunctions.ToSelectList(f => f.Description, 
                                                   f => f.Id.ToString(), 
                                                   " - ");

com

public static class MCVExtentions
{
    public static List<SelectListItem> ToSelectList<T>(
        this IEnumerable<T> enumerable, 
        Func<T, string> text, 
        Func<T, string> value, 
        string defaultOption)
    {
        var items = enumerable.Select(f => new SelectListItem()
                                     {
                                         Text = text(f), 
                                         Value = value(f) 
                                     }).ToList();
        items.Insert(0, new SelectListItem()
                    {
                        Text = defaultOption, 
                        Value = "-1" 
                    });
        return items;
    }
}
Thomas Stock
fonte
legais. Adicionei um Func selecionado, uma opção defaultValue e uma segunda sobrecarga sem nenhum padrão para especificar, e isso funciona como um tratamento. Pela maneira, se você quiser fazer o tipo de retorno IEnumerable <SelectListItem> você pode usar a sintaxe rendimento retorno para fazer este olhar realmente limpo
Chris Meek
48

Usando o construtor que aceita items, dataValueField, dataTextField, selectedValuecomo parâmetros:

ViewData["myList"] = 
                new SelectList(new[] { "10", "15", "25", "50", "100", "1000" }
                .Select(x => new {value = x, text = x}), 
                "value", "text", "15");

Então, na sua opinião:

<%=Html.DropDownList("myList") %>
Çağdaş Tekin
fonte
3
cara legal :) eu gosto! eu estava tentando fazer isso .. mas eu não poderia começar a parte .Select (..) .. nice :) eu vou experimentá-lo em casa, mais tarde :)
Pure.Krome
1
Heya Cagdas. funciona muito bem, exceto que o campo selecionado não está selecionado. qualquer ideia? isso é um bug no material do MVC?
Pure.Krome
2
O item selecionado funciona quando você adiciona o item SelectList ao objeto ViewData (como em ViewData ["myList"] = new SelectList ..) e depois o processa com <% = Html.DropDownList ("myList")%>. Não consegui que o item selecionado funcionasse sem fazê-lo dessa maneira. Esquisito.
Çağdaş Tekin
@Cagdas - oi novamente cara .. eu ainda não descobri isso :( O que é isso sobre um ViewData ??? Eu tenho um ViewData fortemente tipado com sua própria propriedade .... ???
Pure.Krome
@ Pure.Krome - tirei essa propriedade do seu exemplo na pergunta. Agora estou editando o exemplo de código. Por favor, veja minha edição em alguns segundos.
Çağdaş Tekin
19

Criando a resposta de Thomas Stock, criei estes ToSelectListmétodos sobrecarregados :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;

public static partial class Helpers
{
    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, bool selectAll = false)
    {
        return enumerable.ToSelectList(value, value, selectAll);
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, object selectedValue)
    {
        return enumerable.ToSelectList(value, value, new List<object>() { selectedValue });
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, IEnumerable<object> selectedValues)
    {
        return enumerable.ToSelectList(value, value, selectedValues);
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, bool selectAll = false)
    {
        foreach (var f in enumerable.Where(x => x != null))
        {
            yield return new SelectListItem()
            {
                Value = value(f).ToString(),
                Text = text(f).ToString(),
                Selected = selectAll
            };
        }
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, object selectedValue)
    {
        return enumerable.ToSelectList(value, text, new List<object>() { selectedValue });
    }

    public static IEnumerable<SelectListItem> ToSelectList<T>(this IEnumerable<T> enumerable, Func<T, object> value, Func<T, object> text, IEnumerable<object> selectedValues)
    {
        var sel = selectedValues != null
            ? selectedValues.Where(x => x != null).ToList().ConvertAll<string>(x => x.ToString())
            : new List<string>();

        foreach (var f in enumerable.Where(x => x != null))
        {
            yield return new SelectListItem()
            {
                Value = value(f).ToString(),
                Text = text(f).ToString(),
                Selected = sel.Contains(value(f).ToString())
            };
        }
    }
}

No seu controlador, você pode fazer o seguinte:

var pageOptions = new[] { "10", "15", "25", "50", "100", "1000" };
ViewBag.PageOptions = pageOptions.ToSelectList(o => o, "15" /*selectedValue*/);

E, finalmente, na sua visão, coloque:

@Html.DropDownList("PageOptionsDropDown", ViewBag.PageOptions as IEnumerable<SelectListItem>, "(Select one)")

Isso resultará na saída desejada - é claro, você pode deixar de fora a opção "(Select one)"Label acima se não quiser o primeiro item vazio:

<select id="PageOptionsDropDown" name="PageOptionsDropDown">
<option value="">(Select one)</option>
<option value="10">10</option>
<option selected="selected" value="15">15</option>
<option value="25">25</option>
<option value="50">50</option>
<option value="100">100</option>
<option value="1000">1000</option>
</select>

Atualização: Uma lista de códigos revisada pode ser encontrada aqui com comentários XML.

JustinStolle
fonte
13

O problema é que o SelectList funciona como projetado. O bug está no design. Você pode definir a propriedade Selected em SelectedItem, mas isso será completamente ignorado, se você percorrer a lista com o GetEnumerator () (ou se o Mvc fizer isso por você). O Mvc criará novos SelectListItems.

Você deve usar o SelectList ctor com o SelectListItem [], o Nome do texto, o Nome do valor e o Valor selecionado. Lembre-se de passar como SelectedValue o VALUE de SelectListItem, que você deseja selecionar, não o SelectListItem em si! Exemplo:

SelectList sl = new SelectList( new[]{
  new SelectListItem{ Text="one", Value="1"},
  new SelectListItem{ Text="two", Value="2"},
  new SelectListItem{ Text="three", Value="3"}
}, "Text", "Value", "2" );

(não testei isso, mas eu tive o mesmo problema)

a segunda opção obterá o atributo selected = "selected". Isso parece com bons e antigos DataSets ;-)

Gone Coding
fonte
11

Esta é uma opção:

myViewData.PageOptionsDropDown = new[] 
{
 new SelectListItem { Text = "10", Value = "10" },
 new SelectListItem { Text = "15", Value = "15", Selected = true }
 new SelectListItem { Text = "25", Value = "25" },
 new SelectListItem { Text = "50", Value = "50" },
 new SelectListItem { Text = "100", Value = "100" },
 new SelectListItem { Text = "1000", Value = "1000" },
}
murki
fonte
novo SelectListItem {Selected = true, Text = "15", Value = "15"} para o valor selecionado.
Cadoo
oops, sim, esqueci de incluir 'Selected' propriedade, graças Cadoo =)
murki
10

Se isso é literalmente tudo o que você deseja fazer, apenas declarar a matriz como string corrige o problema do item selecionado:

myViewData.PageOptionsDropDown = 
   new SelectList(new string[] {"10", "15", "25", "50", "100", "1000"}, "15");
Algodão mate
fonte
7

É muito simples fazer com que SelectList e SelectedValue funcionem juntos, mesmo que sua propriedade não seja um objeto simples como um valor Int, String ou Double.

Exemplo:

Supondo que nosso objeto Região seja algo como isto:

public class Region {
     public Guid ID { get; set; }
     public Guid Name { get; set; }
}

E seu modelo de exibição é algo como:

public class ContactViewModel {
     public DateTime Date { get; set; }
     public Region Region { get; set; }
     public List<Region> Regions { get; set; }
}

Você pode ter o código abaixo:

@Html.DropDownListFor(x => x.Region, new SelectList(Model.Regions, "ID", "Name")) 

Somente se você substituir o método ToString do objeto Region para algo como:

public class Region {
     public Guid ID { get; set; }
     public Guid Name { get; set; }

     public override string ToString()
     {
         return ID.ToString();
     }
}

Isso tem 100% de garantia para o trabalho.

Mas eu realmente acredito que a melhor maneira de obter o SelectList 100% funcionando em todas as circustâncias é usar o método Equals para testar o valor da propriedade DropDownList ou ListBox em relação a cada item da coleção de itens.

ararog
fonte
5

Parece que, se você tiver uma exibição fortemente digitada, precisará alterar o ID da lista suspensa para que NÃO seja o nome de uma propriedade na classe herdada. Você precisa colocar alguma lógica no método de edição (POST) para extrair o valor selecionado do FORMCollection e colocá-lo na sua instância antes de confirmar suas alterações.

Certamente isso é um pouco estranho, mas eu tentei e funciona.

Portanto, se a sua classe tiver um campo chamado CountryId, digamos, e você estiver exibindo uma lista de nomes de países, faça com que o menu suspenso tenha um ID de CountryName em vez de CountryId. Na postagem, você poderá fazer algo com a Collection ["CountryName"] .


fonte
1
Sim, eu tive esse mesmo problema. Simplesmente mudar o nome funcionou. Obrigado!
Davekaro
4

Eu tive o mesmo problema. A solução é simples. Basta alterar o parâmetro "name" passado ao auxiliar DropDownList para algo que não corresponda a nenhuma das propriedades existentes no seu ViewModel. leia mais aqui: http://www.dotnetguy.co.uk/post/2009/06/25/net-mvc-selectlists-selected-value-does-not-get-set-in-the-view

Cito o Dan Watson:

No MVC, se a visualização for fortemente digitada, a opção selecionada da lista de seleção será substituída e a propriedade de opção selecionada configurada no construtor nunca alcançará a visualização e a primeira opção no menu suspenso será selecionada (por que ainda é um mistério) .

Felicidades!

cleftheris
fonte
3

Todas essas respostas parecem ótimas, mas parece que o controlador está preparando os dados para uma View em um formato estruturado conhecido vs. deixando a visualização simplesmente iterar um IEnumerable <> entregue por meio do modelo e criar uma lista de seleção padrão e deixar DefaultModelBinder devolver o item selecionado a você por meio de um parâmetro de ação. Sim não, porque não? Separação de preocupações, sim? Parece estranho ter o controlador para criar algo tão específico da interface do usuário e da visualização.


fonte
3

Simples:

string[] someName = new string[] {"10", "15", "25", "50", "100", "1000"};
myViewData.PageOptionsDropDown = new SelectList(someName, "15");
Munk
fonte
2

Eu apenas executei assim e não tive problemas,

public class myViewDataObj
    {
        public SelectList PageOptionsDropDown { get; set; }
    }

public ActionResult About()
        {
            myViewDataObj myViewData = new myViewDataObj();
            myViewData.PageOptionsDropDown =
                  new SelectList(new[] { "10", "15", "25", "50", "100", "1000" }, "15");

            ViewData["myList"] = myViewData.PageOptionsDropDown;
            return View();
        }

e

<%=Html.DropDownList("myList") %>

também funcionou se você fizer isso,

public ActionResult About()
        {
            myViewDataObj myViewData = new myViewDataObj();
            myViewData.PageOptionsDropDown =
                  new SelectList(new[] { "10", "15", "25", "50", "100", "1000" });

            ViewData["myListValues"] = myViewData.PageOptionsDropDown;
            ViewData["myList"] = "15";
            return View();
        }

e

<%=Html.DropDownList("myList",(IEnumerable<SelectListItem>)ViewData["myListValues"]) %>
Tony Borf
fonte
1

Usando seu exemplo, isso funcionou para mim:

controlador:

ViewData["PageOptionsDropDown"] = new SelectList(new[] { "10", "15", "25", "50", "100", "1000" }, "15");

Visão:

<%= Html.DropDownList("PageOptionsDropDown")%>
Cadoo
fonte
Sim, isso está funcionando para mim também. No meu caso, a lista é um objeto de modelo de entidade. Controlador: ViewData ["Categories"] = new SelectList (db.Categories.ToList (), "CategoryId", "CategoryName", "15"); Exibir: Html.DropDownList ("CategoryId", (SelectList) ViewData ["Categories"], "--select--")
Junior Mayhé 06/04/10
1
MonthRepository monthRepository = new MonthRepository();
IQueryable<MonthEntity> entities = monthRepository.GetAllMonth();
List<MonthEntity> monthEntities = new List<MonthEntity>();

foreach(var r in entities)
{
    monthEntities.Add(r);
}

ViewData["Month"] = new SelectList(monthEntities, "MonthID", "Month", "Mars");
Cacto
fonte
1

Eu faço assim:

List<SelectListItem> list = new List<SelectListItem>{
new SelectListItem {Selected = true, Text = "Select", Value = "0"},
new SelectListItem {Selected = true, Text = "1", Value = "1"},
new SelectListItem {Selected = true, Text = "2", Value = "2"}
};
return list.ToArray();

O ToArray () cuida dos problemas.

femseks
fonte
1

Se você deseja passar algum texto aleatório para o DropDownList, por exemplo --Selecione--, você pode fazer isso facilmente usando este código:

@Html.DropDownListFor(x => x.CategoryId, new SelectList(Model.Categories, "Id", "Name"), "--Select--", new { @class = "form-control" })
Munique
fonte
0

Pode ser que você tenha alguma ambiguidade no seu ViewData:

Dê uma olhada aqui

KP.
fonte
0

o valor selecionado no modelo aproveita ao invés do item padrão. (Concordo que não li todas as postagens)

Daniel
fonte
0

Não me lembro como o mvc 1 foi configurado, mas parece que ele queria a lista de seleção com o mesmo nome do campo ao qual pertencia ...

O que eu descobri, como alguém disse acima, é que minhas listas de seleção não estavam funcionando no mvc2 quando os ViewData para os quais foram enviados foram nomeados da mesma forma que o campo.

Por exemplo:

<%= Html.DropDownListFor((model => model.ForID), (SelectList)ViewData["ForName"]) %>

funciona quando

<%= Html.DropDownListFor((model => model.ForID), (SelectList)ViewData["ForID"]) %>

não funciona, pois o nome do ViewData "ForID" tem o mesmo nome do campo para o qual está trabalhando

Marca
fonte
0

Uma possível explicação é que o valor da lista de seleção ao qual você está vinculando não é uma sequência.

Então, nesse exemplo, o parâmetro 'PageOptionsDropDown' é uma sequência no seu modelo? Porque se não for, o valor selecionado na lista não será mostrado.

FruitDealer
fonte
0

Se você procurar no código-fonte MVC 2 o método de extensão Html.DropDownList, ele nunca verifica a propriedade SelectVist da classe SelectedValue. Ele apenas tentará combinar com o seu modelo.

Todas as opções acima são todas variações de um tema, ou seja, como você envia um monte de dados para a visualização para uma lista suspensa e todos são tão bons quanto os outros (mais ou menos).

O problema está na vista. Crie seu próprio método de extensão DropDownList que respeite o valor de seleção definido ou repita manualmente. O que sempre funciona melhor para você.

Simon Halsey
fonte
@Franz Antesberger diz a mesma coisa.
Simon Halsey
0

Se você tem uma coleção em seu modelo e sua visualização é do tipo forte, algumas variações disso funcionarão:

@Html.DropDownListFor(x => x.RegionID, 
    new SelectList(Model.Regions,"RegionID", "RegionName", Model.RegionID))

-ou-

@Html.DropDownList("RegionID", 
    new SelectList(Model.Regions, "RegionID", "RegionName", Model.RegionID))
Marca
fonte