В последнее время очень много дискуссий по этой теме. Подобные дорожные блоки встречаются с датами, диапазонами дат и списками флажков с несколькими выборами. В любом месте вы можете использовать богатый набор элементов управления html. Я экспериментировал с концепцией дочерних ViewModels, и я думаю, что решение чище, чем другие подходы, которые я пробовал.
Основная концепция заключается в том, что вы определяете небольшую модель представления, которая тесно связана с пользовательским редактором EditorTemplate.
В вашем примере, мы начнем с (ребенок) ViewModel, что это специфичные для одного списка выбора:
public class SelectModel
{
#region SelectModel(string value, IEnumerable<SelectListItem> items)
public SelectModel(string value, IEnumerable<SelectListItem> items)
{
_value = value;
Items = new List<SelectListItem>(items);
_Select();
}
#endregion
// Properties
public List<SelectListItem> Items { get; private set; }
public string Value
{
get { return _value; }
set { _value = value; _Select();}
}
private string _value;
// Methods
private void _Select()
{
Items.ForEach(x => x.Selected = (Value != null && x.Value == Value));
}
}
В модели представления, которая хочет использовать выпадающее меню вы сочинить в выберите модель (мы» все, используя модели просмотра, верно?):
public class EmailModel
{
// Constructors
public EmailModel()
{
Priority = new SelectModel("normal", _ToPrioritySelectItems());
}
// Properties
public SelectModel Priority { get; set; }
// Methods
private IEnumerable<SelectListItem> _ToPrioritySelectItems()
{
List<SelectListItem> result = new List<SelectListItem>();
result.Add(new SelectListItem() { Text = "High", Value = "high" });
...
}
Обратите внимание, что это простой пример с фиксированным набором выпадающих элементов. Если они поступают с уровня домена, контроллер передает их в ViewModel.
Затем добавьте шаблон редактор SelectModel.ascx в Shared/EditorTemplates
<%@ Control Inherits="System.Web.Mvc.ViewUserControl<SelectModel>" %>
<div class="set">
<%= Html.LabelFor(model => model) %>
<select id="<%= ViewData.ModelMetadata.PropertyName %>_Value" name="<%=ViewData.ModelMetadata.PropertyName %>.Value">
<% foreach (var item in Model.Items) { %>
<%= Html.OptionFor(item) %>
<% } %>
</select>
</div>
Примечание: OptionFor обычай расширение, которое делает очевидным
Хитрость здесь в том, что идентификатор и имя устанавливается с помощью составной формат, который ожидает ModelBinder по умолчанию. В нашем примере «Priority.Value». Таким образом, свойство Value на основе строки, которое определено как часть SelectModel, устанавливается непосредственно. Установщик заботится об обновлении списка элементов для установки опции выбора по умолчанию, если нам нужно повторно отобразить форму.
Если этот подход «детской модели» действительно близок, это более сложные «контрольные фрагменты разметки». Теперь у меня есть модели детского просмотра, которые следуют аналогичному подходу для списков MultiSelect, диапазонов дат начала и окончания и комбинаций даты и времени.
Как только вы спуститесь по этому пути, следующий очевидный вопрос станет подтверждением.
Я закончил тем, что все мой ребенок ViewModel реализуют стандартный интерфейс:
public interface IValidatable
{
bool HasValue { get; }
bool IsValid { get; }
}
Тогда у меня есть обычай ValidationAttribute:
public class IsValidAttribute : ValidationAttribute
{
// Constructors
public IsValidAttribute()
{
ErrorMessage = "(not valid)";
}
// Properties
public bool IsRequired { get; set; }
// Methods
private bool Is(object value)
{
return value != null && !"".Equals(value);
}
public override bool IsValid(object value)
{
if (!Is(value) && !IsRequired)
return true;
if (!(value is IValidatable))
throw new InvalidOperationException("IsValidAttribute requires underlying property to implement IValidatable");
IValidatable validatable = value as IValidatable;
return validatable.IsValid;
}
}
Теперь вы можете просто поставить атрибуты на свойствах, которые child ViewModel основан как любое скалярное свойство:
[IsValid(ErrorMessage = "Please enter a valid start date/time")]
public DateAndTimeModel Start { get; set; }