一般來說,我在設計我的視圖模型時避免使用枚舉,因爲它們不使用ASP.NET MVC的助手和開箱即用的模型聯編程序。它們在你的域模型中是完美的,但對於你可以使用其他類型的視圖模型。所以我離開了我的映射層,它負責在我的域模型之間來回轉換,並查看模型來擔心這些轉換。
這就是說,如果由於某種原因,你決定在這種情況下使用枚舉你可以滾自定義模型粘合劑:
public class NotificationDeliveryTypeModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (value != null)
{
var rawValues = value.RawValue as string[];
if (rawValues != null)
{
NotificationDeliveryType result;
if (Enum.TryParse<NotificationDeliveryType>(string.Join(",", rawValues), out result))
{
return result;
}
}
}
return base.BindModel(controllerContext, bindingContext);
}
}
將在的Application_Start註冊:
ModelBinders.Binders.Add(
typeof(NotificationDeliveryType),
new NotificationDeliveryTypeModelBinder()
);
所以非常好。現在,標準的東西:
視圖模型:
[Flags]
public enum NotificationDeliveryType
{
InSystem = 1,
Email = 2,
Text = 4
}
public class MyViewModel
{
public IEnumerable<NotificationDeliveryType> Notifications { get; set; }
}
控制器:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel
{
Notifications = new[]
{
NotificationDeliveryType.Email,
NotificationDeliveryType.InSystem | NotificationDeliveryType.Text
}
};
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
視圖(~/Views/Home/Index.cshtml
):爲NotificationDeliveryType
(~/Views/Shared/EditorTemplates/NotificationDeliveryType.cshtml
)
@model MyViewModel
@using (Html.BeginForm())
{
<table>
<thead>
<tr>
<th>Notification</th>
</tr>
</thead>
<tbody>
@Html.EditorFor(x => x.Notifications)
</tbody>
</table>
<button type="submit">OK</button>
}
自定義編輯器模板:
@model NotificationDeliveryType
<tr>
<td>
@foreach (NotificationDeliveryType item in Enum.GetValues(typeof(NotificationDeliveryType)))
{
<label for="@ViewData.TemplateInfo.GetFullHtmlFieldId(item.ToString())">@item</label>
<input type="checkbox" id="@ViewData.TemplateInfo.GetFullHtmlFieldId(item.ToString())" name="@(ViewData.TemplateInfo.GetFullHtmlFieldName(""))" value="@item" @Html.Raw((Model & item) == item ? "checked=\"checked\"" : "") />
}
</td>
</tr>
很明顯,在編輯器模板中編寫這樣的代碼的軟件開發人員(我在這種情況下)不應該爲他的工作感到驕傲。我的意思是看看它!即使是我在5分鐘前編寫這個Razor模板的東西,也不能理解它的功能。
所以我們重構的可重複使用的自定義HTML幫助這個麪條代碼:
public static class HtmlExtensions
{
public static IHtmlString CheckBoxesForEnumModel<TModel>(this HtmlHelper<TModel> htmlHelper)
{
if (!typeof(TModel).IsEnum)
{
throw new ArgumentException("this helper can only be used with enums");
}
var sb = new StringBuilder();
foreach (Enum item in Enum.GetValues(typeof(TModel)))
{
var ti = htmlHelper.ViewData.TemplateInfo;
var id = ti.GetFullHtmlFieldId(item.ToString());
var name = ti.GetFullHtmlFieldName(string.Empty);
var label = new TagBuilder("label");
label.Attributes["for"] = id;
label.SetInnerText(item.ToString());
sb.AppendLine(label.ToString());
var checkbox = new TagBuilder("input");
checkbox.Attributes["id"] = id;
checkbox.Attributes["name"] = name;
checkbox.Attributes["type"] = "checkbox";
checkbox.Attributes["value"] = item.ToString();
var model = htmlHelper.ViewData.Model as Enum;
if (model.HasFlag(item))
{
checkbox.Attributes["checked"] = "checked";
}
sb.AppendLine(checkbox.ToString());
}
return new HtmlString(sb.ToString());
}
}
,我們清理混亂中我們的編輯模板:
@model NotificationDeliveryType
<tr>
<td>
@Html.CheckBoxesForEnumModel()
</td>
</tr>
其產生的表:
現在顯然它應該是nic e如果我們可以爲這些複選框提供更友好的標籤。例如像:
[Flags]
public enum NotificationDeliveryType
{
[Display(Name = "in da system")]
InSystem = 1,
[Display(Name = "@")]
Email = 2,
[Display(Name = "txt")]
Text = 4
}
我們所要做的就是適應我們寫的HTML幫助早期:
var field = item.GetType().GetField(item.ToString());
var display = field
.GetCustomAttributes(typeof(DisplayAttribute), true)
.FirstOrDefault() as DisplayAttribute;
if (display != null)
{
label.SetInnerText(display.Name);
}
else
{
label.SetInnerText(item.ToString());
}
這給了我們一個更好的結果:
是否有特殊原因的自定義需要ModelBinder的?到目前爲止,我還沒有接受可以爲null的枚舉或枚舉列表並正確解析它的ViewModel的問題。 – 2012-02-13 20:55:01
@ Splash-X,它是一個標記的枚舉(標有'[Flags]')屬性的枚舉。所以你需要能夠對它進行按位操作。我不確定默認模型聯編程序是否可以處理此問題。無論如何,我不在我的視圖模型中使用枚舉。 – 2012-02-13 20:57:03
哇...謝謝!太棒了!關於使用枚舉的觀點,我同意......這只是一個典型的標誌,並且確定我缺少一些默認的模型綁定器... – Rikon 2012-02-13 21:23:27