Я потратил большую часть прошлой недели на колено в новой функциональности шаблонов, запеченной в MVC2. Мне было трудно найти шаблон DropDownList. Самая большая проблема, с которой я работал, - это получить исходные данные для выпадающего списка в шаблон. Я видел много примеров, где вы можете поместить исходные данные в словарь ViewData (ViewData [ "DropDownSourceValuesKey" ]), а затем получить их в самом шаблоне (var sourceValues = ViewData [ "DropDownSourceValuesKey" ];) Это работает, но я сделал не похоже на наличие глупой струны в качестве штыря линч для выполнения этой работы.
Ниже приведен подход, который я придумал, и хотел получить мнения относительно этого подхода:
вот мои цели дизайна:
- Модель просмотра должна содержать исходные данные для выпадающего списка
- Предельные глупые строки
- Не использовать словарь ViewDatali >
- Контроллер отвечает за заполнение свойства исходными данными для выпадающего списка
Здесь моя модель просмотра:
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; }
}
Модель My View имеет свойство CustomerCode, которое является значением, которое пользователь выбирает из списка значений. У меня есть свойство CustomerCodeList, которое является списком возможных значений CustomerCode и является источником для выпадающего списка. Я создал атрибут DropDownList с DropDownListTargetProperty. DropDownListTargetProperty указывает на свойство, которое будет заполнено на основе пользовательского выбора из сгенерированного раскрывающегося списка (в данном случае это свойство CustomerCode).
Обратите внимание на, что свойство CustomerCode имеет значение [ScaffoldColumn (false)], которое заставляет генератор пропускать поле в сгенерированном выходе.
В моем файле DropDownList.ascx будет создан элемент формы с раскрывающимся списком с исходными данными из свойства CustomerCodeList. Сгенерированный выпадающий список будет использовать значение DropDownListTargetProperty из атрибута DropDownList в качестве атрибутов Id и Name элемента Select form. Таким образом, сгенерированный код будет выглядеть так:
<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 и Name сгенерированного элемента Select.
ПРИМЕЧАНИЕ. Вы должны переопределить метод CreateMetadata DataAnnotationsModelMetadataProvider для DropDownListAttribute. Вот код для этого:
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;
}
}
Затем вам нужно сделать вызов новому метадатчику в приложении Application_Start из Global.asax.cs:
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ModelMetadataProviders.Current = new MetadataProvider();
}
Хорошо, я надеюсь, что это имеет смысл, и я надеюсь, что этот подход может сэкономить вам некоторое время. Мне хотелось бы получить отклик на этот подход. Есть ли лучший подход?