【发布时间】:2010-04-29 19:50:23
【问题描述】:
在过去一周的大部分时间里,我一直在深入研究 MVC2 中包含的新模板功能。我很难让 DropDownList 模板正常工作。我一直在努力解决的最大问题是如何将下拉列表的源数据获取到模板中。我看到很多示例,您可以将源数据放入 ViewData 字典 (ViewData["DropDownSourceValuesKey"]) 然后在模板本身中检索它们 (var sourceValues = ViewData["DropDownSourceValuesKey"];) 这有效,但我做到了不喜欢用一根愚蠢的绳子作为完成这项工作的关键。
以下是我想出的一种方法,并希望获得对此方法的意见:
这是我的设计目标:
- 视图模型应包含下拉列表的源数据
- 限制愚蠢的字符串
- 不使用 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 的值作为 Select 表单元素的 Id 和 Name 属性。所以生成的代码会是这样的:
<select id="CustomerCode" name="CustomerCode">
<option>...
</select>
这很有效,因为当提交表单时,MVC 将使用下拉列表中的选定值填充目标属性,因为生成的下拉列表的名称是目标属性。我将其可视化为 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 助手,但它不允许我更改生成的 Select 元素的 Id 和 Name 属性。
注意:您必须为 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();
}
嗯,我希望这是有道理的,我希望这种方法可以为您节省一些时间。我想要一些关于这种方法的反馈。有更好的方法吗?
【问题讨论】:
标签: asp.net templates asp.net-mvc-2 drop-down-menu