Большую часть прошедшей недели я провел по колено в новой функциональности шаблонов, встроенной в 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; }
}
My View Model имеет свойство CustomerCode, которое является значением, которое пользователь выбирает из списка значений. У меня есть свойство CustomerCodeList, которое представляет собой список возможных значений CustomerCode и является источником раскрывающегося списка. Я создал атрибут DropDownList с DropDownListTargetProperty. DropDownListTargetProperty указывает на свойство, которое будет заполнено на основе выбора пользователя из сгенерированного раскрывающегося списка (в данном случае, свойства CustomerCode).
Обратите внимание , что свойство CustomerCode имеет [ScaffoldColumn (false)], что заставляет генератор пропускать поле в сгенерированном выводе.
Мой файл DropDownList.ascx сгенерирует элемент формы выпадающего списка с исходными данными из свойства CustomerCodeList. Сгенерированный раскрывающийся список будет использовать значение DropDownListTargetProperty из атрибута DropDownList в качестве атрибутов Id и Name элемента формы Select. Таким образом, сгенерированный код будет выглядеть так:
<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();
}
Ну, я надеюсь, что это имеет смысл, и я надеюсь, что этот подход может сэкономить вам время. Я хотел бы получить некоторые отзывы об этом подходе, пожалуйста. Есть ли лучший подход?