2012-04-12 15 views
6

Il mio modello è la seguente:su post, un elenco a discesa SelectList.SelectedValue è nullo

public class testCreateModel 
{ 
    public string s1 { get; set; } 
    public SelectList DL { get; set; } 

    public testCreateModel() 
    { 
     Dictionary<string, string> items = new Dictionary<string, string>(); 
     items.Add("1", "Item 1"); 
     items.Add("2", "Item 2"); 
     DL = new SelectList(items, "Key", "Value"); 
    } 
} 

Le mie azioni avvio è:

public ActionResult testCreate() 
    { 
     testCreateModel model = new testCreateModel(); 
     return View(model); 
    } 

mio punto di vista Razor (parti irrilevanti cancellate) è:

@model Tasks.Models.testCreateModel 

@using (Html.BeginForm()) { 
<fieldset> 
    <legend>testCreateModel</legend> 

    <div class="editor-label"> 
     @Html.LabelFor(model => model.s1) 
    </div> 
    <div class="editor-field"> 
     @Html.EditorFor(model => model.s1) 
    </div> 

    <div class="editor-label"> 
     Select an item: 
    </div> 
    <div class="editor-field"> 
     @Html.DropDownList("dropdownlist", (SelectList)Model.DL) 
    </div> 

    <p> 
     <input type="submit" value="Create" /> 
    </p> 
</fieldset> 
} 

L'azione palo è:

public ActionResult testCreate(testCreateModel model, FormCollection collection) 
    { 
     if (ModelState.IsValid) 
     { 
      Console.WriteLine("SelectedValue: ",model.DL.SelectedValue); 
      Console.WriteLine("FormCollection:", collection["dropdownlist"]); 
      // update database here... 
     } 
     return View(model); 
    } 

Sul retro del post, model.DL.SelectedValue è null. (Tuttavia, l'elemento selezionato può essere ottenuto da FormCollection, ma questo è oltre il punto). L'oggetto DL è ancora adeguatamente popolata in caso contrario, l'uscita Finestra Immediata come segue:

model.DL 
{System.Web.Mvc.SelectList} 
    base {System.Web.Mvc.MultiSelectList}: {System.Web.Mvc.SelectList} 
    SelectedValue: null 
model.DL.Items 
Count = 2 
    [0]: {[1, Item 1]} 
    [1]: {[2, Item 2]} 
model.DL.SelectedValue 
null 

Q1: Come posso fare uso della proprietà SelectedValue invece?

Ora, se nella vista Razor a cambiare il nome del tag HTML SELECT per DL (ad esempio, come il nome della proprietà nel modello):

@Html.DropDownList("DL", (SelectList)Model.DL) 

ottengo un'eccezione:

No parameterless constructor defined for this object. 
Stack Trace: 
[MissingMethodException: No parameterless constructor defined for this object.] 
System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0 
System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98 
System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241 
System.Activator.CreateInstance(Type type, Boolean nonPublic) +69 
System.Web.Mvc.DefaultModelBinder.CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType) +199 
System.Web.Mvc.DefaultModelBinder.BindSimpleModel(ControllerContext controllerContext, ModelBindingContext bindingContext, ValueProviderResult 
... 

Q2: perché?

Grazie.

risposta

11

MVC restituirà solo il valore dell'opzione selezionata nel POST, quindi è necessario che una proprietà contenga il valore singolo restituito.

Come un buon consiglio, provare a selezionare Elenchi tramite ViewBag, che consente di mantenere i vostri ViewModels puliti dai dati che devono compilare il modulo.

Così il vostro esempio può essere risolto in questo modo:

public class testCreateModel 
{ 
    public string s1 { get; set; } 
    public int SelectedValue { get; set; } 
} 

e nella vista solo fare questo:

@Html.DropDownList("SelectedValue", (SelectList)ViewBag.DL) 

prima del popolamento ViewBag.DL nella vostra azione GET.

quanto riguarda la tua Q2, il default ModelBinder richiede che tutti i tipi di legarsi ad avere un costruttore di default (in modo che il ModelBinder può creare loro)

+0

Grazie per il metodo suggerito di utilizzare un elenco a discesa. Funziona e c'è meno lavoro necessario per raccogliere il valore selezionato! –

+0

Grazie, questo è il miglior post che mi ha cancellato riguardo l'utilizzo di dropdownlist in mvc –

10

è stata selezionata una risposta, ma guarda come ho fatto. Di seguito è riportato il codice come faccio normalmente quando si inserisce un menu a discesa. È molto semplicistico, ti suggerisco di usarlo come base per costruire i tuoi drop down.

Nella parte superiore del mio punto di vista ho specificare mio punto di vista il modello:

@model MyProject.ViewModels.MyViewModel 

Il mio punto di vista ho un elenco a discesa che visualizza tutte le banche che un utente può scegliere tra:

<table> 
    <tr> 
      <td><b>Bank:</b></td> 
      <td> 
       @Html.DropDownListFor(
        x => x.BankId, 
        new SelectList(Model.Banks, "Id", "Name", Model.BankId), 
        "-- Select --" 
       ) 
       @Html.ValidationMessageFor(x => x.BankId) 
      </td> 
    </tr> 
</table> 

Ho sempre un modello di vista per una vista, non ho mai passato un oggetto dominio direttamente alla vista. In questo caso la mia vista del modello contiene un elenco delle banche che sarà popolata dal database:

public class MyViewModel 
{ 
    // Other properties 

    public int BankId { get; set; } 
    public IEnumerable<Bank> Banks { get; set; } 
} 

La mia banca modello di dominio:

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

Poi nel mio metodo di azione ho creare un'istanza della mia visualizza il modello e compila l'elenco dei banchi dal database. Una volta fatto questo, restituisco il modello della vista alla vista:

public ActionResult MyActionMethod() 
{ 
    MyViewModel viewModel = new ViewModel 
    { 
      // Database call to get all the banks 
      // GetAll returns a list of Bank objects 
      Banks = bankService.GetAll() 
    }; 

    return View(viewModel); 
} 

[HttpPost] 
public ActionResult MyActionMethod(MyViewModel viewModel) 
{ 
    // If you have selected an item then BankId would have a value in it 
} 

Spero che questo aiuti.

+0

Eccellente e grazie per la condivisione. Tuttavia, ho difficoltà a recuperare i dati nel modulo di postback. Qualche idea? Grazie –

Problemi correlati