我已经花了上周的大部分时间膝盖深入新的模板功能烘焙到MVC2。我很难尝试使用DropDownList模板。我一直在努力解决的最大问题是如何将下拉列表的源数据提供给模板。我看到很多例子,你可以将源数据放入ViewData字典(ViewData [“DropDownSourceValuesKey”]),然后在模板本身中检索它们(var sourceValues = ViewData [“DropDownSourceValuesKey”];)这有效,但我不像有一个愚蠢的字符串作为使这项工作的关键。MVC2 EditorTemplate为DropDownList
下面是一个方法,我想出来的,并希望得到这一做法的意见:
这里是我的设计目标:
- 视图模型应包含降源数据下拉列表
- 限制傻字符串
- 不使用ViewData字典
- 控制器负责下来填充与源数据的属性下降名单
这里是我的视图模型:
public class CustomerViewModel
{
[ScaffoldColumn(false)]
public String CustomerCode{ get; set; }
[UIHint("DropDownList")]
[DropDownList(DropDownListTargetProperty = "CustomerCode"]
[DisplayName("Customer Code")]
public IEnumerable<SelectListItem> CustomerCodeList { get; set; }
public String FirstName { get; set; }
public String LastName { get; set; }
public String PhoneNumber { get; set; }
public String Address1 { get; set; }
public String Address2 { get; set; }
public String City { get; set; }
public String State { get; set; }
public String Zip { get; set; }
}
我的视图模型具有CustomerCode属性,该属性是一个值,用户从值列表中选择。我有一个CustomerCodeList属性,它是可能的CustomerCode值的列表,并且是下拉列表的来源。我用DropDownListTargetProperty创建了一个DropDownList属性。 DropDownListTargetProperty指向将根据用户从生成的下拉列表中选择(本例中为CustomerCode属性)进行填充的属性。
注意 CustomerCode属性具有[ScaffoldColumn(false)],强制生成器跳过生成的输出中的字段。
我的DropDownList.ascx文件将使用CustomerCodeList属性中的源数据生成下拉列表表单元素。生成的下拉列表将使用DropDownList属性中的DropDownListTargetProperty的值作为选择表单元素的Id和Name属性。所以生成的代码如下所示:
<select id="CustomerCode" name="CustomerCode">
<option>...
</select>
这工作了巨大的,因为提交表单时,MVC会从下拉列表填充所选值目标属性,因为产生的下拉列表中的名称IS的目标属性。我有点想象它,因为CustomerCodeList属性是CustomerCode属性的一种扩展。我已将源数据耦合到该属性。
这里是我的控制器代码:
public ActionResult Create()
{
//retrieve CustomerCodes from a datasource of your choosing
List<CustomerCode> customerCodeList = modelService.GetCustomerCodeList();
CustomerViewModel viewModel= new CustomerViewModel();
viewModel.CustomerCodeList = customerCodeList.Select(s => new SelectListItem() { Text = s.CustomerCode, Value = s.CustomerCode, Selected = (s.CustomerCode == viewModel.CustomerCode) }).AsEnumerable();
return View(viewModel);
}
这里是我的DropDownListAttribute代码:
namespace AutoForm.Attributes
{
public class DropDownListAttribute : Attribute
{
public String DropDownListTargetProperty { get; set; }
}
}
这里是我的模板(DropDownList的代码。ASCX):
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IEnumerable<SelectListItem>>" %>
<%@ Import Namespace="AutoForm.Attributes"%>
<script runat="server">
DropDownListAttribute GetDropDownListAttribute()
{
var dropDownListAttribute = new DropDownListAttribute();
if (ViewData.ModelMetadata.AdditionalValues.ContainsKey("DropDownListAttribute"))
{
dropDownListAttribute = (DropDownListAttribute)ViewData.ModelMetadata.AdditionalValues["DropDownListAttribute"];
}
return dropDownListAttribute;
}
</script>
<% DropDownListAttribute attribute = GetDropDownListAttribute();%>
<select id="<%= attribute.DropDownListTargetProperty %>" name="<%= attribute.DropDownListTargetProperty %>">
<% foreach(SelectListItem item in ViewData.Model)
{%>
<% if (item.Selected == true) {%>
<option value="<%= item.Value %>" selected="true"><%= item.Text %></option>
<% } %>
<% else {%>
<option value="<%= item.Value %>"><%= item.Text %></option>
<% } %>
<% } %>
</select>
我尝试使用Html.DropDownList帮手,但它不会让我改变ID和产生的选择元素的名称属性。
注意:您必须覆盖DropDownListAttribute的DataAnnotationsModelMetadataProvider的CreateMetadata方法。下面是该代码:
public class MetadataProvider : DataAnnotationsModelMetadataProvider
{
protected override ModelMetadata CreateMetadata(IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName)
{
var metadata = base.CreateMetadata(attributes, containerType, modelAccessor, modelType, propertyName);
var additionalValues = attributes.OfType<DropDownListAttribute>().FirstOrDefault();
if (additionalValues != null)
{
metadata.AdditionalValues.Add("DropDownListAttribute", additionalValues);
}
return metadata;
}
}
然后你不得不在Global.asax.cs中的的Application_Start新MetadataProvider打电话:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelMetadataProviders.Current = new MetadataProvider();
}
嗯,我希望这是有道理的,我希望这种方法可以为你节省一些时间。我希望得到关于这种方法的一些反馈。有更好的方法吗?
,想到的第一件事是创建ServicePackageWithOwnerName自定义编辑模板。 ServicePackageWithOwnerName自定义模板将为OwnerName属性发出一个下拉框。在MaintainServicePackageViewModel中,您将使用UIHint(“[ServicePackageWithOwnerName Tempalte Name]”)装饰CurrentServicePackage。我正在计划探索这个概念,但还没有时间这样做。 – tschreck 2010-05-12 20:53:59
杰克 - 看看这个链接,它可能是你需要的: http://dotnetslackers.com/articles/aspnet/asp-net-mvc-2-0-templating.aspx – tschreck 2010-05-14 04:31:12
此外,请检查此链接。 Brad Wilson创建了一个非常好的模板系列。该链接处理嵌套的复杂类型。 http://bradwilson.typepad.com/blog/2009/10/aspnet-mvc-2-templates-part-4-custom-object-templates.html – tschreck 2010-05-14 15:04:57