枚举标志的模型绑定列表 [英] Model Bind List of Enum Flags

查看:360
本文介绍了枚举标志的模型绑定列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有枚举标志网格中,每个记录复选框,以确定该记录的标志值的一排。这是通知列表系统提供,用户可以选择(每一个)他们是如何希望他们交付:

I have a grid of Enum Flags in which each record is a row of checkboxes to determine that record's flag values. This is a list of notifications that the system offers and the user can pick (for each one) how they want them delivered:

[Flag]
public enum NotificationDeliveryType
{
  InSystem = 1,
  Email = 2,
  Text = 4
}

我发现这个文章,但他又回到一个标志值和他结合它在这样的控制器(带一个星期几的概念):

I found this article but he's getting back a single flag value and he's binding it in the controller like this (with a days of the week concept):

[HttpPost]
public ActionResult MyPostedPage(MyModel model)
{
  //I moved the logic for setting this into a helper 
  //because this could be re-used elsewhere.
  model.WeekDays = Enum<DayOfWeek>.ParseToEnumFlag(Request.Form, "WeekDays[]");
  ...
}

我找不到任何地方的MVC 3模型绑定可以处理的标志。谢谢!

I can't find anywhere that the MVC 3 model binder can handle flags. Thanks!

推荐答案

在一般我避免设计我的看法模型时,因为它们不与ASP.NET MVC的助手和开箱即用模型绑定的发挥使用枚举。他们是在你的领域模型完美的罚款,但对于视图模型,你可以使用其它类型。所以,我把我的映射层是负责来回转换我的域模型和视图模型之间担心的转换。

In general I avoid using enums when designing my view models because they don't play with ASP.NET MVC's helpers and out of the box model binder. They are perfectly fine in your domain models but for view models you could use other types. So I leave my mapping layer which is responsible to convert back and forth between my domain models and view models to worry about those conversions.

这是说,如果由于某种原因你决定在这种情况下使用枚举你可以滚自定义模型绑定:

This being said, if for some reason you decide to use enums in this situation you could roll a custom model binder:

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进行注册:

which will be registered in Application_Start:

ModelBinders.Binders.Add(
    typeof(NotificationDeliveryType), 
    new NotificationDeliveryTypeModelBinder()
);

到目前为止好。现在,标准的东西:

So far so good. Now the standard stuff:

查看模式:

[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);
    }
}

查看(〜/查看/主页/ Index.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>
}

NotificationDeliveryType 自定义编辑模板(〜/查看/共享/ EditorTemplates / NotificationDeliveryType.cshtml

@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>

很明显,一个软件开发者(我在这种情况下)在编辑器中的模板写这样code不应该是非常满意自己的工作。我的意思是看t将其!即使我是写像5分钟前该剃刀模板不能再理解它做什么。

It's obvious that a software developer (me in this case) writing such code in an editor template shouldn't be very proud of his work. I mean look t it! Even I that wrote this Razor template like 5 minutes ago can no longer understand what it does.

因此​​,我们重构这个意大利面条code在可重复使用的自定义HTML助手:

So we refactor this spaghetti code in a reusable custom HTML helper:

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());
    }
}

和我们清理混乱中我们的编辑模板:

and we clean the mess in our editor template:

@model NotificationDeliveryType
<tr>
    <td>
        @Html.CheckBoxesForEnumModel()
    </td>
</tr>

这将产生表:

现在显然这本来是很好,如果我们能为这些复选框提供友好的标签。就像例如:

Now obviously it would have been nice if we could provide friendlier labels for those checkboxes. Like for example:

[Flags]
public enum NotificationDeliveryType
{
    [Display(Name = "in da system")]
    InSystem = 1,

    [Display(Name = "@")]
    Email = 2,

    [Display(Name = "txt")]
    Text = 4
}

我们所要做的是适应我们写的HTML辅助早期:

All we have to do is adapt the HTML helper we wrote earlier:

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());
}

这给了我们一个更好的结果:

which gives us a better result:

这篇关于枚举标志的模型绑定列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆