ASP.NET MVC Html.DropDownList SelectedValue

103

Ich habe versucht, dies ist RC1 und dann auf RC2 aktualisiert, wodurch das Problem nicht behoben wurde.

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

Ergebnis: Die SelectedValue-Eigenschaft wird für das Objekt festgelegt

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

Ergebnis: Alle erwarteten Optionen werden auf dem Client gerendert, das ausgewählte Attribut wird jedoch nicht festgelegt. Das Element in SelectedValue ist in der Liste vorhanden, das erste Element in der Liste ist jedoch standardmäßig ausgewählt.

Wie soll ich das machen?

Update Dank der Antwort von John Feminella habe ich herausgefunden, wo das Problem liegt. "UserId" ist eine Eigenschaft in dem Modell, in das meine Ansicht stark typisiert ist. Wenn Html.DropDownList ("UserId" in einen anderen Namen als "UserId" geändert wird, wird der ausgewählte Wert korrekt gerendert.

Dies führt jedoch dazu, dass der Wert nicht an das Modell gebunden ist.

blau
quelle
Sind Sie sicher, dass der Wert in der Liste enthalten ist?
John Sheehan
Das Problem besteht weiterhin in Version 1 von ASP.NET MVC
Mathias F
Was ist selectedUserId!?
Fulvio
Wie sollen Sie den Wert bei der Aktualisierung binden, wenn der Name Ihrer Eingabe nicht mit Ihrer Eigenschaft übereinstimmt?
Ryudice

Antworten:

68

So habe ich dieses Problem behoben:

Ich hatte folgendes:

Regler:

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

Aussicht

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

Geändert durch:

Aussicht

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

Es scheint, dass DropDown nicht denselben Namen haben darf wie ViewData: S seltsam, aber es hat funktioniert.

Sanchitos
quelle
19
+1 für "der DropDown muss nicht den gleichen Namen haben hat den ViewData Namen" Big Thanks
Daniel Dyson
Danke!!! Sie haben gerade mein Problem gelöst, nachdem Sie sich viele Stunden lang gefragt haben, warum dies nicht funktioniert hat :)
Jen
1
Dies funktioniert für den ausgewählten Wert, aber ein Problem tritt auf, wenn Sie versuchen, "DealerTypes" in der Datenbank zu aktualisieren, da die DropDownList jetzt an "DealerTypesDD" gebunden ist, die im Modell nicht vorhanden ist. Eine Problemumgehung besteht darin, der DropDownList ein ausgeblendetes Feld und htmlAttributes hinzuzufügen: <input type = "hidden" name = "DealerTypes" id = "DealerTypes" value = "" /> <% = Html.DropDownList ("DealerTypesDD", ViewData [ "DealerTypes"] als SelectList, neu {@onchange = "DealerTypes.value = this.value"})%>
Zim
3
Sie sollten dem Namen der
Ansichtsdaten
1
Was zum Teufel! Dies ist in MVC5 nicht behoben ??? Kopieren Sie als weiteren von @Paul Hatcher erwähnten Trick die ausgewählte ID in die ViewData ["DealerTypes"].
Jsgoupil
51

Versuche dies:

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

Und dann:

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

Mit MVC RC2 bekomme ich:

<select id="PeopleClass" name="PeopleClass">
    <option value="1">Name1</option>
    <option selected="selected" value="2">Name2</option>
    <option value="3">Name3</option>
</select>
John Feminella
quelle
Dies war hilfreich, um die Ursache aufzuspüren. Ich habe meine Frage aktualisiert, um die Ergänzungen widerzuspiegeln.
Blu
ist zuerst im Modell? Zweiter im Controller? und drittens im Blick ist es klar, aber 2 erste .. ???
rr
Gute Antwort, aber ist es nicht besser, ein ViewModel zu verwenden?
Jess
@Jess: Ich habe diese Antwort im März 2009 vor über 5 Jahren geschrieben. Die Zeiten haben sich geändert! Fühlen Sie sich frei zu aktualisieren. :)
John Feminella
5

Sie können DropDown weiterhin als "UserId" bezeichnen und die Modellbindung funktioniert weiterhin ordnungsgemäß.

Damit dies funktioniert, muss der ViewData-Schlüssel, der die SelectList enthält, nicht denselben Namen wie die Model-Eigenschaft haben, die Sie binden möchten. In Ihrem speziellen Fall wäre dies:

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

Dadurch wird ein Auswahlelement mit dem Namen UserId erstellt, das denselben Namen wie die UserId-Eigenschaft in Ihrem Modell hat. Daher wird es vom Modellbinder mit dem Wert festgelegt, der im vom Html.DropDownList-Helfer generierten Auswahlelement des HTML-Codes ausgewählt wurde.

Ich bin nicht sicher, warum dieser bestimmte Html.DropDownList-Konstruktor den in der SelectList angegebenen Wert nicht auswählt, wenn Sie die Auswahlliste in die ViewData mit einem Schlüssel einfügen, der dem Eigenschaftsnamen entspricht. Ich vermute, es hat etwas damit zu tun, wie der DropDownList-Helfer in anderen Szenarien verwendet wird, in denen die Konvention lautet, dass Sie in den ViewData eine SelectList mit demselben Namen wie die Eigenschaft in Ihrem Modell haben. Dies wird korrekt funktionieren:

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

// in my view
<%=Html.DropDownList("UserId")%>
Rui
quelle
Vielen Dank Kumpel, vor allem Antworten, die für mich funktionierten, weiß nicht warum, aber es tat
Lamin Sanneh
Schließlich! Nach stundenlangem Versuch fand ich endlich Ihre "einzige Anforderung". Das hat es geschafft.
SteveCav
2

Wenn wir nicht glauben, dass dies ein Fehler ist, den das Team beheben sollte, sollte MSDN das Dokument zumindest verbessern. Das Verwirrende kommt wirklich von dem schlechten Dokument davon. In MSDN wird der Name des Parameters wie folgt erläutert :

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

Dies bedeutet nur, dass das endgültige HTML, das es generiert, diesen Parameter als Namen der Auswahleingabe verwendet. Aber es bedeutet tatsächlich mehr als das.

Ich denke, der Designer geht davon aus, dass der Benutzer ein Ansichtsmodell verwendet, um die Dropdown-Liste anzuzeigen, und auch den Post zurück zu derselben verwendet Ansichtsmodell verwendet. In vielen Fällen folgen wir dieser Annahme jedoch nicht wirklich.

Verwenden Sie das obige Beispiel,

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

Wenn wir der Annahme folgen, sollten wir ein Ansichtsmodell für diese Dropdown-Liste definieren

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

Denn wenn Post zurück, nur der ausgewählte Wert Post zurück wird, so dass er es annehmen sollte das Modell der Eigenschaft SelectedPersonId Post zurück, die Html.DropDownList erster Parameter bedeutet Name soll ‚SelectedPersonId‘ sein. Der Designer ist daher der Ansicht, dass beim Anzeigen der Modellansicht in der Ansicht die Modelleigenschaft SelectedPersonId den Standardwert dieser Dropdown-Liste enthalten sollte. Obwohl Ihre List <SelectListItem> -Personen bereits das Flag Selected gesetzt haben, um anzugeben, welche ausgewählt / standardmäßig ist, ignoriert die tml.DropDownList dies tatsächlich und erstellt ihre eigene IEnumerable <SelectListItem> neu und legt das Standard- / ausgewählte Element basierend auf dem Namen fest.

Hier ist der Code von 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);
}

Der Code ging also tatsächlich weiter und versuchte nicht nur, den Namen im Modell nachzuschlagen, sondern auch in den Ansichtsdaten. Sobald er einen findet, wird die selectList neu erstellt und Ihre ursprüngliche Selected ignoriert.

Das Problem ist, dass wir es in vielen Fällen nicht wirklich so verwenden. Wir wollen nur eine Auswahlliste mit einem / mehreren Elementen einwerfen. Ausgewählte Menge wahr.

Natürlich ist die Lösung einfach. Verwenden Sie einen Namen, der weder im Modell noch in den Ansichtsdaten enthalten ist. Wenn keine Übereinstimmung gefunden werden kann, wird die ursprüngliche Auswahlliste verwendet, und die ursprüngliche Auswahl wird wirksam.

Aber ich denke immer noch, dass mvc es verbessern sollte, indem es eine weitere Bedingung hinzufügt

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

Denn wenn die ursprüngliche selectList bereits eine Selected hatte, warum sollten Sie das ignorieren?

Nur meine Gedanken.

Liuhongbo
quelle
Dies ist wirklich die beste Erklärung, die ich bisher gelesen habe. Hat mir viel Kopfschmerzen erspart. Ich musste nur den Wert der viewmodel-Eigenschaft festlegen und es funktionierte. Vielen Dank.
Moses Machua
2

Der Code im vorherigen MVC 3-Beitrag funktioniert nicht, ist aber ein guter Anfang. Ich werde es reparieren. Ich habe diesen Code getestet und er funktioniert in MVC 3 Razor C # Dieser Code verwendet das ViewModel-Muster, um eine Eigenschaft zu füllen, die a zurückgibt List<SelectListItem>.

Die Modellklasse

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

Die ViewModel-Klasse

using System.Web.Mvc;

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

Die Controller-Methode

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

Die Aussicht

@model MvcApp.ViewModels.ProductListViewModel

@using (Html.BeginForm())
{
    @Html.DropDownList("Products", Model.Products)
}

Die HTML-Ausgabe wird so etwas wie sein

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

abhängig davon, wie Sie die Ausgabe formatieren. Ich hoffe das hilft. Der Code funktioniert.

wayne.blackmon
quelle
1
Dies setzt die selectedOption nicht.
Jess
Um das ausgewählte Element mit SelectListItem festzulegen, setzen Sie die Selected-Eigenschaft auf true.
Wayne.blackmon
1

Dies scheint ein Fehler in der SelectExtensions-Klasse zu sein, da nur die ViewData und nicht das Modell für das ausgewählte Element überprüft werden. Der Trick besteht also darin, das ausgewählte Element aus dem Modell unter dem Namen der Eigenschaft in die ViewData-Auflistung zu kopieren.

Dies geht aus der Antwort hervor, die ich in den MVC-Foren gegeben habe. Ich habe auch eine vollständigere Antwort in einem Blog-Beitrag , der das DropDownList-Attribut von Kazi verwendet ...

Gegeben ein Modell

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

und ein grundlegendes Ansichtsmodell von

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

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

Dann schreiben wir eine DropDownList-Editor-Vorlage wie folgt.

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

Dies funktioniert auch, wenn Sie ArticleType im Ansichtsmodell in ein SelectListItem ändern. Sie müssen jedoch einen Typkonverter gemäß Kazis Blog implementieren und registrieren, um den Ordner zu zwingen, dies als einfachen Typ zu behandeln.

In Ihrem Controller haben wir dann ...

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);
     }
     ...
}
Paul Hatcher
quelle
Vielen Dank für die Antwort, ich muss das überprüfen.
Blu
0

Das Problem ist, dass Dropboxen nicht wie Listboxen funktionieren, zumindest so, wie es das ASP.NET MVC2-Design erwartet: Eine Dropbox lässt nur null oder einen Wert zu, da Listboxen eine Auswahl mit mehreren Werten haben können. Aus Gründen der HTML-Genauigkeit sollte dieser Wert nicht in der Optionsliste als "ausgewähltes" Flag, sondern in der Eingabe selbst enthalten sein.

Siehe folgendes Beispiel:

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

Für die Kombination ist die Option ausgewählt, aber auch das Wertattribut festgelegt . Wenn Sie also möchten, dass ASP.NET MVC2 eine Dropbox rendert und auch einen bestimmten Wert ausgewählt hat (z. B. Standardwerte usw.), sollten Sie ihm beim Rendern einen Wert wie folgt geben:

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

In ASP.NET MVC 3 können Sie Ihre Liste einfach zu ViewData hinzufügen ...

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;

... und dann in Ihrer Rasiermesseransicht namentlich referenzieren ...

@Html.DropDownList("options")

Sie müssen die Liste im DropDownList-Aufruf nicht manuell "verwenden". Wenn Sie dies auf diese Weise tun, stellen Sie den ausgewählten Wert auch für mich richtig ein.

Haftungsausschluss:

  1. Ich habe dies nicht mit der Web Forms View Engine versucht, aber es sollte auch funktionieren.
  2. Ich habe dies in Version 1 und Version 2 nicht getestet, aber es könnte funktionieren.
John Bubriski
quelle
0

Ich habe es geschafft, das gewünschte Ergebnis zu erzielen, aber mit einem etwas anderen Ansatz. In der Dropdown-Liste habe ich das Modell verwendet und dann darauf verwiesen. Ich bin mir nicht sicher, ob Sie danach gesucht haben.

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

Model.Matters.FeeStructures oben ist meine ID, die Ihr Wert des Elements sein kann, das ausgewählt werden soll.

Jukes
quelle