Asp.net 用于DropDownList的MVC2编辑器模板
在过去一周的大部分时间里,我都在深入研究MVC2中的新模板功能。我很难让DropDownList模板正常工作。我一直在努力解决的最大问题是如何将下拉列表的源数据获取到模板中。我看到了很多示例,其中您可以将源数据放入ViewData字典(ViewData[“DropDownSourceValuesKey”]),然后在模板本身中检索它们(var sourceValues=ViewData[“DropDownSourceValuesKey”];)这是可行的,但我不喜欢使用愚蠢的字符串作为实现这一功能的lynch pin 下面是我提出的一种方法,我想就这种方法征求意见: 以下是我的设计目标:Asp.net 用于DropDownList的MVC2编辑器模板,asp.net,templates,asp.net-mvc-2,drop-down-menu,Asp.net,Templates,Asp.net Mvc 2,Drop Down Menu,在过去一周的大部分时间里,我都在深入研究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; }
}
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);
}
namespace AutoForm.Attributes
{
public class DropDownListAttribute : Attribute
{
public String DropDownListTargetProperty { get; set; }
}
}
<%@ 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>
以下是我的模板代码(DropDownList.ascx):
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; }
}
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);
}
namespace AutoForm.Attributes
{
public class DropDownListAttribute : Attribute
{
public String DropDownListTargetProperty { get; set; }
}
}
<%@ 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>
嗯,我希望这是有意义的,我希望这种方法可以为您节省一些时间。我想要一些关于这个方法的反馈。有更好的方法吗?很好。这就是我要找的。谢谢 但您的示例模型是简单模型。像这样的复杂视图模型怎么样
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; }
}
公共类MaintainServicePackageViewModel
{
公共IEnumerable ServicePackageWithOwnerName{get;set;}
public ServicePackageWithOwnerName CurrentServicePackage{get;set;}
公共IEnumerable服务包{get;set;}
}
公共类ServicePackageWithOwnerName:ServicePackage
{
[UIHint(“DropDownList”)]
[DropDownList(DropDownListTargetProperty=“Owner”)]
[DisplayNameLocalized(typeof(Resources.Globalization),“OwnerName”)]
公共IEnumerable所有者名称{get;set;}
}
OwnerName设置为dropdownlist,但它不是viewmodel的直接元素,而是ServicePackageWithOwnerName的子元素,后者是viewmodel的元素。在这种情况下,无法在控制器中设置OwnerName值,如何解决这个问题?谢谢
问候
Jack我想我找到了一个解决方案,可以让它在使用Html.EditorForModel()时正常工作;当使用EditorForModel()时,MVC使用Object.ascx循环遍历模型的所有属性,并为模型中的每个属性调用相应的模板。ASP.Net MVC开箱即用的代码中有Object.ascx,但您可以创建自己的Object.ascx。只需在共享视图文件夹中创建一个EditorTemplates子文件夹。在那里创建Object.ascx文件。(有关更多信息,请阅读本文:) 这是我的Object.ascx:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Import Namespace="WebAppSolutions.Helpers" %>
<% if (ViewData.TemplateInfo.TemplateDepth > 1) { %>
<%= ViewData.ModelMetadata.SimpleDisplayText%>
<% }
else { %>
<% foreach (var prop in ViewData.ModelMetadata.Properties.Where(pm => pm.ShowForEdit && !ViewData.TemplateInfo.Visited(pm))) { %>
<% var htmlFieldName = Html.HtmlFieldNameFor(prop.PropertyName);%>
<% if (prop.HideSurroundingHtml) { %>
<%= Html.Editor(htmlFieldName)%>
<% }
else { %>
<div id="<%= htmlFieldName %>Container" class="editor-field">
<% if (!String.IsNullOrEmpty(Html.Label(prop.PropertyName).ToHtmlString())) { %>
<%= Html.Label(prop.PropertyName, Html.HtmlDisplayName(prop.PropertyName), prop.IsRequired)%>
<% } %>
<%= Html.Editor(prop.PropertyName, "", htmlFieldName)%>
<%= Html.ValidationMessage(prop.PropertyName, "*") %>
</div>
<% } %>
<% } %>
1) { %>
pm.ShowForEdit&!ViewData.TemplateInfo.Visited(pm)){%>
我的WebAppSolutions中有一些自定义代码。HtmlFieldNameFor和HtmlDisplayName的帮助程序。这些帮助程序从应用于视图模型中属性的属性检索数据
public static String HtmlFieldNameFor<TModel>(this HtmlHelper<TModel> html, String propertyName)
{
ModelMetadata modelMetaData = GetModelMetaData(html, propertyName);
return GetHtmlFieldName(modelMetaData, propertyName);
}
public static String HtmlDisplayName<TModel>(this HtmlHelper<TModel> html, String propertyName)
{
ModelMetadata modelMetaData = GetModelMetaData(html, propertyName);
return modelMetaData.DisplayName ?? propertyName;
}
private static ModelMetadata GetModelMetaData<TModel>(HtmlHelper<TModel> html, String propertyName)
{
ModelMetadata modelMetaData = ModelMetadata.FromStringExpression(propertyName, html.ViewData);
return modelMetaData;
}
private static String GetHtmlFieldName(ModelMetadata modelMetaData, string defaultHtmlFieldName)
{
PropertyExtendedMetaDataAttribute propertyExtendedMetaDataAttribute = GetPropertyExtendedMetaDataAttribute(modelMetaData);
return propertyExtendedMetaDataAttribute.HtmlFieldName ?? defaultHtmlFieldName;
}
公共静态字符串HtmlFieldName for(此HtmlHelper html,字符串属性名称)
{
ModelMetadata ModelMetadata=GetModelMetaData(html,propertyName);
返回GetHtmlFieldName(modelMetaData,propertyName);
}
公共静态字符串HtmlDisplayName(此HtmlHelper html,字符串propertyName)
{
ModelMetadata ModelMetadata=GetModelMetaData(html,propertyName);
返回modelMetaData.DisplayName??propertyName;
}
私有静态模型元数据GetModelMetaData(HtmlHelper html,字符串propertyName)
{
ModelMetadata ModelMetadata=ModelMetadata.FromStringExpression(propertyName,html.ViewData);
返回模型元数据;
}
私有静态字符串GetHtmlFieldName(ModelMetadata ModelMetadata,字符串defaultHtmlFieldName)
{
PropertyExtendedMetaDataAttribute PropertyExtendedMetaDataAttribute=GetPropertyExtendedMetaDataAttribute(modelMetaData);
返回propertyExtendedMetaDataAttribute.HtmlFieldName??defaultHtmlFieldName;
}
使用EditorModelFor()实现此功能的关键是(应该是上面Object.ascx中的第20行左右):
prop.PropertyName是ViewModel中包含将成为DropDownList的数据列表的属性。htmlFieldName是DropDownList属性正在替换的隐藏属性的名称。有意义吗
我希望这能对您有所帮助。这是我在代码项目的这篇文章中的方法:
首先想到的是为ServicePackageWithOwnerName创建一个自定义编辑器模板。ServicePackageWithOwnerName自定义模板将为OwnerName属性发出一个下拉框。在MaintainServicePackageViewModel中,您将使用UIHint(“[ServicePackageWithOwnerName Tempalte Name]”装饰CurrentServicePackage。我正计划探索这个概念,但还没有时间。杰克-查看此链接,它可能是您需要的:另外,请查看此链接。Brad Wilson创建了一个非常好的模板制作系列。此链接处理嵌套的复杂类型。您可以参考
PropertyExtendedMetaDataAttribute PropertyExtendedDMEtaDataAttribute=GetPropertyExtendedMetaDataAttribute(modelMetaData);
,但我在任何地方都找不到该类型。这是您创建的其他类型还是在某些第三方库(如MvcContrib)中?如果您难以接受您的答案,请标记此帖子以引起版主注意。