Jak mogę sprawić, aby ta lista wyboru ASP.NET MVC działała?

126

Tworzę listę selectList w moim kontrolerze, aby wyświetlić ją w widoku.

Próbuję stworzyć to w locie, coś w rodzaju ... w ten sposób ...

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

Kompiluje się, ale wynik jest zły ...

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

Zauważ, że żaden element nie jest wybrany?

Jak mogę to naprawić?

Pure.Krome
źródło
1
Sześć odpowiedzi ... jeden Ulubione ... Nie upvotes: / dam mu +1
womp

Odpowiedzi:

128

Tak to robię

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

Na pierwszy rzut oka nie jestem pewien, czy wiem, czego szukasz ...

mhenrixon
źródło
2
Dziękuję, że pomogło mi to zrozumieć.
Cadoo
6
Nie rozwiązuje problemu z wybranym przedmiotem, ale jest to świetny sposób na uniknięcie magicznych ciągów dla wybranych list! Pozdrawiam
Alan Christensen
11
Aby rozwiązać problem z wybranym elementem, dodaj następujący kod: SelectList sList = new SelectList(selectList, "Text", "Value", selected);gdzie selectedjest aktualnie wybrany klient
jao.
68

Używam metody rozszerzenia:

stosowanie

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

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

z

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
źródło
miły. Dodałem wybraną Func, opcję defaultValue i drugie przeciążenie bez żadnych wartości domyślnych do określenia, a to działa świetnie. Nawiasem mówiąc, jeśli chcesz, aby zwracany typ IEnumerable <SelectListItem>, możesz użyć składni zwrotu zysku, aby wyglądał naprawdę czysto
Chris Meek
48

Używając konstruktora, który akceptuje items, dataValueField, dataTextField, selectedValuejako parametry:

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

Zatem Twoim zdaniem:

<%=Html.DropDownList("myList") %>
Çağdaş Tekin
źródło
3
miły koleś :) lubię! próbowałem to zrobić .. ale nie mogłem dostać części .Select (..) .. fajnie :) Wypróbuję to w domu, później :)
Pure.Krome
1
Heya Cagdas. działa świetnie, z wyjątkiem tego, że wybrane pole nie jest zaznaczone. dowolny pomysł? czy to jest błąd w sprawach MVC?
Pure.Krome
2
Wybrany element działa, gdy dodasz element SelectList do obiektu ViewData (jak w ViewData ["myList"] = new SelectList ..), a następnie wyrenderujesz go za pomocą <% = Html.DropDownList ("myList")%>. Nie mogłem jednak zmusić wybranego elementu do pracy bez zrobienia tego w ten sposób. Dziwne.
Çağdaş Tekin
@Cagdas - cześć jeszcze raz koleś ... nadal tego nie rozgryzłem :( Co to jest z ViewData ??? Mam silnie wpisane ViewData z własną własnością .... ???
Pure.Krome
@ Pure.Krome - wziąłem tę właściwość z twojego przykładu w pytaniu. Teraz edytuję przykładowy kod. Proszę zobaczyć moją zmianę za kilka sekund.
Çağdaş Tekin
19

Opierając się na odpowiedzi Thomasa Stocka, stworzyłem te przeciążone ToSelectListmetody:

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

W kontrolerze możesz wykonać następujące czynności:

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

I na koniec w swoim Widoku umieść:

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

Daje to pożądane wyjście - oczywiście możesz "(Select one)"pominąć powyższą opcję Etykieta, jeśli nie chcesz, aby pierwszy pusty element:

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

Aktualizacja: poprawioną listę kodu można znaleźć tutaj z komentarzami XML.

JustinStolle
źródło
13

Problem w tym, że SelectList działa zgodnie z projektem. Błąd tkwi w projekcie. Możesz ustawić Selected Property w SelectedItem, ale zostanie to całkowicie zignorowane, jeśli przejdziesz przez listę za pomocą GetEnumerator () (lub jeśli Mvc zrobi to za Ciebie). Mvc utworzy zamiast tego nowe SelectListItems.

Musisz użyć wskaźnika SelectList z SelectListItem [], Text-Name, Value-Name i SelectedValue. Pamiętaj, aby przekazać jako SelectedValue wartość SelectListItem, którą chcesz wybrać, a nie sam SelectListItem! Przykład:

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" );

(nie testowałem tego, ale miałem ten sam problem)

wtedy druga opcja otrzyma atrybut selected = "selected". To wygląda jak stare, dobre zestawy danych ;-)

Gone Coding
źródło
11

To jest opcja:

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
źródło
new SelectListItem {Selected = true, Text = "15", Value = "15"} dla wybranej wartości.
Cadoo
Ups, tak, zapomniałem dołączyć właściwość „Wybrana”, dzięki Cadoo =)
murki
10

Jeśli to dosłownie wszystko, co chcesz zrobić, po prostu zadeklarowanie tablicy jako ciągu znaków rozwiązuje problem z wybranym elementem:

myViewData.PageOptionsDropDown = 
   new SelectList(new string[] {"10", "15", "25", "50", "100", "1000"}, "15");
Matowa bawełna
źródło
7

Bardzo łatwo jest sprawić, aby SelectList i SelectedValue współpracowały ze sobą, nawet jeśli twoja właściwość nie jest prostym obiektem, takim jak wartość Int, String lub Double.

Przykład:

Zakładając, że nasz obiekt Region jest mniej więcej taki:

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

Twój model widoku to coś takiego:

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

Możesz mieć poniższy kod:

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

Tylko jeśli nadpisujesz metodę ToString obiektu Region na coś takiego:

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

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

To ma 100% gwarancję działania.

Ale naprawdę uważam, że najlepszym sposobem, aby SelectList działał w 100% we wszystkich przypadkach, jest użycie metody Equals w celu przetestowania wartości właściwości DropDownList lub ListBox dla każdego elementu w kolekcji elementów.

ararog
źródło
5

Wygląda na to, że jeśli masz mocno wpisany widok, musisz zmienić identyfikator menu, aby NIE był to nazwa właściwości w dziedziczonej klasie. Następnie musisz wprowadzić pewną logikę do swojej metody edit (POST), aby pobrać wybraną wartość z FORMCollection i umieścić ją w swojej instancji przed zatwierdzeniem zmian.

Jest to z pewnością trochę dziwne, ale próbowałem i działa.

Więc jeśli Twoja klasa ma pole o nazwie CountryId powiedz, i wyświetlasz listę nazw krajów, spraw, aby menu zawierało identyfikator CountryName zamiast CountryId, a następnie w poście możesz coś zrobić z Collection ["CountryName"] .


źródło
1
Tak, miałem ten sam problem. Po prostu zmiana nazwy zadziałała. Dzięki!
davekaro,
4

Miałem dokładnie ten sam problem. Rozwiązanie jest proste. Po prostu zmień parametr „name” przekazany do pomocnika DropDownList na coś, co nie pasuje do żadnej z właściwości istniejących w Twoim ViewModel. czytaj więcej tutaj: http://www.dotnetguy.co.uk/post/2009/06/25/net-mvc-selectlists-selected-value-does-not-get-set-in-the-view

Cytuję Dana Watsona:

W MVC, jeśli widok jest silnie wpisany, wybrana opcja listy wyboru zostanie nadpisana, a właściwość wybranej opcji ustawiona w konstruktorze nigdy nie osiągnie widoku, a zamiast tego zostanie wybrana pierwsza opcja z listy rozwijanej (dlaczego nadal jest trochę tajemnicą) .

Twoje zdrowie!

cleftheris
źródło
3

Wszystkie te odpowiedzi wyglądają świetnie, ale wydaje się, że kontroler przygotowuje dane do widoku w dobrze znanym formacie strukturalnym, zamiast pozwolić widokowi po prostu iterować IEnumerable <> dostarczony za pośrednictwem modelu i zbudować standardową listę wyboru, a następnie pozwolić DefaultModelBinder dostarczyć wybraną pozycję z powrotem do Ciebie za pośrednictwem parametru działania. Tak, nie, czemu nie? Rozdzielenie obaw, tak? Wydaje się dziwne, że kontroler buduje coś tak specyficznego dla interfejsu użytkownika i widoku.


źródło
3

Prosty:

string[] someName = new string[] {"10", "15", "25", "50", "100", "1000"};
myViewData.PageOptionsDropDown = new SelectList(someName, "15");
Munk
źródło
2

Po prostu uruchomiłem to w ten sposób i nie miałem żadnych problemów,

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

i

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

zadziałało też, jeśli to zrobisz,

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

i

<%=Html.DropDownList("myList",(IEnumerable<SelectListItem>)ViewData["myListValues"]) %>
Tony Borf
źródło
1

Używając twojego przykładu, zadziałało to dla mnie:

kontroler:

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

widok:

<%= Html.DropDownList("PageOptionsDropDown")%>
Cadoo
źródło
Tak, to też działa na mnie. W moim przypadku lista jest obiektem Entity Model. Kontroler: ViewData ["Categories"] = new SelectList (db.Categories.ToList (), "CategoryId", "CategoryName", "15"); Widok: Html.DropDownList ("CategoryId", (SelectList) ViewData ["Categories"], "--select--")
Junior Mayhé
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");
Kaktus
źródło
1

Robię to tak:

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();

ToArray () rozwiązuje problemy.

femseks
źródło
1

Jeśli chcesz przekazać losowy tekst do swojej DropDownList, na przykład - Wybierz - możesz to łatwo zrobić za pomocą tego kodu:

@Html.DropDownListFor(x => x.CategoryId, new SelectList(Model.Categories, "Id", "Name"), "--Select--", new { @class = "form-control" })
Monachium
źródło
0

Może się zdarzyć, że masz pewną niejednoznaczność w swoich ViewData:

Spójrz tutaj

KP.
źródło
0

wartość wybrana w modelu ma zastosowanie zamiast pozycji domyślnej. (Zgadzam się, nie przeczytałem wszystkich postów)

Daniel
źródło
0

Nie pamiętam, jak skonfigurowano mvc 1, ale wygląda na to, że lista wyboru chciała mieć taką samą nazwę jak pole, do którego należy ...

To, co znalazłem, jak ktoś powiedział powyżej, to to, że moje listy wyboru nie działały w mvc2, gdy ViewData, które zostały wysłane, nosiły taką samą nazwę jak pole.

Na przykład:

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

działa, kiedy

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

nie działa, ponieważ nazwa ViewData „ForID” ma taką samą nazwę jak pole, dla którego pracuje

znak
źródło
0

Możliwym wyjaśnieniem jest to, że wartość listy wyboru, z którą się łączysz, nie jest ciągiem.

Czy w tym przykładzie parametr „PageOptionsDropDown” jest ciągiem znaków w Twoim modelu? Bo jeśli tak nie jest, to wybrana wartość na liście nie zostanie pokazana.

FruitDealer
źródło
0

Jeśli spojrzysz na kod źródłowy MVC 2 w metodzie rozszerzenia Html.DropDownList, nigdy nie sprawdza ona właściwości SelectedValue klasy SelectList. Będzie tylko próbował dopasować się do Twojego Modelu.

Wszystkie powyższe są wszystkimi odmianami tematu, tj. Jak wysłać zbiór danych do widoku do listy rozwijanej i wszystkie są tak dobre jak inne (mniej więcej).

Problem jest w widoku. Utwórz własną metodę rozszerzenia DropDownList, która nie respektuje ustawionej przez Ciebie wartości select lub wykonaj iterację ręcznie. Który kiedykolwiek działa najlepiej dla Ciebie.

Simon Halsey
źródło
@Franz Antesberger mówi bardzo to samo.
Simon Halsey,
0

Jeśli masz kolekcję w swoim modelu, a Twój widok jest silnie typem, niektóre odmiany będą działać:

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

-lub-

@Html.DropDownList("RegionID", 
    new SelectList(Model.Regions, "RegionID", "RegionName", Model.RegionID))
znak
źródło