2011-04-14 67 views
1

DropDownLists可能是我最不喜欢使用MVC框架的部分。我的表单中有几个下拉列表,我需要将选定的值传递给接受模型作为其参数的ActionResult。MVC DropDownList值发布到模型没有绑定

的标记看起来是这样的:

<div class="editor-label"> 
    @Html.LabelFor(model => model.FileType) 
</div> 
<div class="editor-field"> 
    @Html.DropDownListFor(model => model.FileType.Variety, (SelectList)ViewBag.FileTypes) 
</div> 

<div class="editor-label"> 
    @Html.LabelFor(model => model.Status) 
</div> 
<div class="editor-field"> 
    @Html.DropDownListFor(model => model.Status.Status, (SelectList)ViewBag.Status) 
</div> 

我的控制器操作是这样的:

[HttpPost] 
public ActionResult Create(int reviewid, ReviewedFile file) 
{ 
    if (ModelState.IsValid) 
    { 
     UpdateModel(file); 
    } 

    //repository.Add(file); 

    return RedirectToAction("Files", "Reviews", new { reviewid = reviewid, id = file.ReviewedFileId }); 
} 

这应该是所有好,除了从下拉菜单中的值好被张贴空值。当我深入了解一下的ModelState错误,原因是发现:

从类型 “System.String”输入 “PeerCodeReview.Models.OutcomeStatus” 参数转换失败,因为没有类型转换器可 在这些类型之间进行转换。

它不应该这么难,但它是。所以问题是,我需要做什么才能正确地绑定我的模型属性?另外,我知道我可以传递一个FormCollection对象,但这意味着要更改当前期望强类型模型参数的单元测试的重要部分。

+0

此外,你不需要说的UpdateModel(文件)在那里,张贴表单值已经包含在你的'ReviewedFile file'参数。 – 2011-04-14 03:10:32

+0

我只是需要它来检查ModelState错误。一旦我弄清楚如何排除问题,我们就会去做。 – 2011-04-14 04:18:48

回答

2

试试这个:

<div class="editor-label"> 
    @Html.LabelFor(model => model.FileType) 
</div> 
<div class="editor-field"> 
    @Html.DropDownListFor(model => model.FileType.Id, (SelectList)ViewBag.FileTypes) 
</div> 

<div class="editor-label"> 
    @Html.LabelFor(model => model.Status) 
</div> 
<div class="editor-field"> 
    @Html.DropDownListFor(model => model.Status.Id, (SelectList)ViewBag.Status) 
</div> 
+0

很好的建议,但我已经尝试过了,同样的错误仍然存​​在 - MVC不知道如何将字符串值转换为相应的对象属性(例如,评论文件.FileType) – 2011-04-14 04:24:36

+0

我不能重复这一点。我已经设置了一个快速项目并模仿了你的类,但是MVC将字符串值转换为对象属性。例如,如果我进入Create操作并检查file.FileType的值,那么我得到一个'FileType'类型的对象,其值为'{Id = 2,Variety = null}'。它可能不是完整的对象,但有足够的信息从回购中检索它。它究竟为你做了什么? – 2011-04-14 13:16:26

3

您需要为绑定到下拉列表的两个属性创建并注册自定义模型联编程序。

这里是我的模型绑定我建了整整为此代码:

public class LookupModelBinder<TModel> : DefaultModelBinder 
    where TModel : class 
{ 
    private string _key; 

    public LookupModelBinder(string key = null) 
    { 
     _key = key ?? typeof(TModel).Name; 
    } 

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) 
    { 
     var dbSession = ((IControllerWithSession)controllerContext.Controller).DbSession; 

     var modelName = bindingContext.ModelName; 
     TModel model = null; 
     ValueProviderResult vpResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); 
     if (vpResult != null) 
     { 
      bindingContext.ModelState.SetModelValue(modelName, vpResult); 
      var id = (int?)vpResult.ConvertTo(typeof(int)); 
      model = id == null ? null : dbSession.Get<TModel>(id.Value); 
     } 
     if (model == null) 
     { 
      ModelValidator requiredValidator = ModelValidatorProviders.Providers.GetValidators(bindingContext.ModelMetadata, controllerContext).Where(v => v.IsRequired).FirstOrDefault(); 
      if (requiredValidator != null) 
      { 
       foreach (ModelValidationResult validationResult in requiredValidator.Validate(bindingContext.Model)) 
       { 
        bindingContext.ModelState.AddModelError(modelName, validationResult.Message); 
       } 
      } 
     } 
     return model; 
    } 
} 

TModel是下拉框应绑定到属性的类型。在我的应用程序中,下拉框给出数据库中对象的Id,因此此模型绑定器将使用该ID,从数据库中检索正确的实体,然后返回该实体。您可能有不同的方式将由下拉列表给出的字符串转换为模型的正确实体。

您还需要在Global.asax中注册模型联编程序。

binders[typeof(EmploymentType)] = new LookupModelBinder<EmploymentType>(); 

这假设下拉列表控件的名称与类型名称相同。如果没有,您可以将密钥传递给模型联编程序。

binders[typeof(EmploymentType)] = new LookupModelBinder<EmploymentType>("ControlName"); 
+0

+1用于自定义活页夹。那个石头。 – Farray 2011-07-12 21:44:19