DropDownList的MVC2 EditorTemplate

tsc*_*eck 16 asp.net templates asp.net-mvc-2 drop-down-menu

过去一周的大部分时间都花在了MVC2的新模板功能上.我很难尝试使用DropDownList模板.我一直在努力解决的最大问题是如何将下拉列表的源数据提供给模板.我看了很多例子,你可以将源数据放在ViewData字典中(ViewData ["DropDownSourceValuesKey"])然后在模板本身中检索它们(var sourceValues = ViewData ["DropDownSourceValuesKey"];)这样可行,但我做了不喜欢有一个愚蠢的字符串作为使这项工作的lynch引脚.

以下是我提出的一种方法,希望对此方法有所了解:

这是我的设计目标:

  • 视图模型应包含下拉列表的源数据
  • 限制傻弦
  • 不使用ViewData字典
  • Controller负责使用下拉列表的源数据填充属性

这是我的视图模型:

   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; }
    }
Run Code Online (Sandbox Code Playgroud)

My View Model具有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>
Run Code Online (Sandbox Code Playgroud)

这工作了巨大的,因为提交表单时,MVC会因为生成的下拉列表中的名称填充从下拉列表中选定值目标属性IS target属性.我有点想象它,因为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);
}
Run Code Online (Sandbox Code Playgroud)

这是DropDownListAttribute的代码:

namespace AutoForm.Attributes
{
    public class DropDownListAttribute : Attribute
    {
        public String DropDownListTargetProperty { get; set; }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是我的模板代码(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>
Run Code Online (Sandbox Code Playgroud)

我尝试使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你必须在Global.asax.cs的Application_Start中调用新的MetadataProvider:

protected void Application_Start()
{
    RegisterRoutes(RouteTable.Routes);

    ModelMetadataProviders.Current = new MetadataProvider();
}
Run Code Online (Sandbox Code Playgroud)

嗯,我希望这是有道理的,我希望这种方法可以节省你一些时间.我想请一些关于这种方法的反馈.有更好的方法吗?

小智 1

完美的。这就是我正在寻找的。谢谢!

但您的示例模型是简单模型。像这样的复杂视图模型怎么样?

public class MaintainServicePackageViewModel
{
    public IEnumerable<ServicePackageWithOwnerName> ServicePackageWithOwnerName { get; set; }
    public ServicePackageWithOwnerName CurrentServicePackage { get; set; }
    public IEnumerable<ServiceWithPackageName> ServiceInPackage { get; set; }
}

public class ServicePackageWithOwnerName : ServicePackage
{
    [UIHint("DropDownList")]
    [DropDownList(DropDownListTargetProperty = "Owner")]
    [DisplayNameLocalized(typeof(Resources.Globalization), "OwnerName")]
    public IEnumerable<SelectListItem> OwnerName { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

OwnerName 设置为下拉列表,但它不是视图模型的直接元素,而是 ServicePackageWithOwnerName 的子元素,而 ServicePackageWithOwnerName 是视图模型的元素。在这种情况下,无法在控制器中设置 OwnerName 值,如何解决这个问题?欣赏!

问候

杰克