使用复选框来回发带有标志的枚举

Using checkboxes to PostBack Enum with Flags

我有一个枚举 属性,我正在尝试通过复选框设置它的值。枚举被标记,当用户选择多个选项时,我希望 属性 将所有选定的标志连接起来。

我尝试为每个枚举值添加一个复选框,并为每个复选框指定相同的名称。在回发期间,第一个选择的标志被检索但不与其他标志连接。

我能否以某种方式解决这个问题,而无需为每个标志单独设置 属性?

型号

public class HomeModel
{
    public Fruit MyFruits { get; set; }
}

[Flags] public enum Fruit
{
    Love = 1,
    Joy = 2,
    Peace = 4,
    Patience = 8,
    Kindness = 16,
    Goodness = 32,
    Faithfulness = 64,
    Gentleness = 128,
    SelfControl = 256
}

查看

@model EnumFlagTest.Models.HomeModel
@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Index</title>
</head>
<body>
    <div>
        @using (Html.BeginForm())
        {
            <h1>Fruits</h1>

            <div><label>@EnumFlagTest.Models.Fruit.Love.ToString() <input type="checkbox" name="MyFruits" value="@((int) EnumFlagTest.Models.Fruit.Love)" @(Model.MyFruits.HasFlag(EnumFlagTest.Models.Fruit.Love) ? "checked" : String.Empty) /></label></div>
            <div><label>@EnumFlagTest.Models.Fruit.Joy.ToString() <input type="checkbox" name="MyFruits" value="@((int) EnumFlagTest.Models.Fruit.Joy)" @(Model.MyFruits.HasFlag(EnumFlagTest.Models.Fruit.Joy) ? "checked" : String.Empty) /></label></div>
            <div><label>@EnumFlagTest.Models.Fruit.Peace.ToString() <input type="checkbox" name="MyFruits" value="@((int) EnumFlagTest.Models.Fruit.Peace)" @(Model.MyFruits.HasFlag(EnumFlagTest.Models.Fruit.Peace) ? "checked" : String.Empty) /></label></div>
            <div><label>@EnumFlagTest.Models.Fruit.Patience.ToString() <input type="checkbox" name="MyFruits" value="@((int) EnumFlagTest.Models.Fruit.Patience)" @(Model.MyFruits.HasFlag(EnumFlagTest.Models.Fruit.Patience) ? "checked" : String.Empty) /></label></div>
            <div><label>@EnumFlagTest.Models.Fruit.Kindness.ToString() <input type="checkbox" name="MyFruits" value="@((int) EnumFlagTest.Models.Fruit.Kindness)" @(Model.MyFruits.HasFlag(EnumFlagTest.Models.Fruit.Kindness) ? "checked" : String.Empty) /></label></div>
            <div><label>@EnumFlagTest.Models.Fruit.Goodness.ToString() <input type="checkbox" name="MyFruits" value="@((int) EnumFlagTest.Models.Fruit.Goodness)" @(Model.MyFruits.HasFlag(EnumFlagTest.Models.Fruit.Goodness) ? "checked" : String.Empty) /></label></div>
            <div><label>@EnumFlagTest.Models.Fruit.Faithfulness.ToString() <input type="checkbox" name="MyFruits" value="@((int) EnumFlagTest.Models.Fruit.Faithfulness)" @(Model.MyFruits.HasFlag(EnumFlagTest.Models.Fruit.Faithfulness) ? "checked" : String.Empty) /></label></div>
            <div><label>@EnumFlagTest.Models.Fruit.Gentleness.ToString() <input type="checkbox" name="MyFruits" value="@((int) EnumFlagTest.Models.Fruit.Gentleness)" @(Model.MyFruits.HasFlag(EnumFlagTest.Models.Fruit.Gentleness) ? "checked" : String.Empty) /></label></div>
            <div><label>@EnumFlagTest.Models.Fruit.SelfControl.ToString() <input type="checkbox" name="MyFruits" value="@((int) EnumFlagTest.Models.Fruit.SelfControl)" @(Model.MyFruits.HasFlag(EnumFlagTest.Models.Fruit.SelfControl) ? "checked" : String.Empty) /></label></div>

            <input type="submit" value="GO" />
        }
    </div>
</body>
</html>

控制器

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
    HomeModel model = new HomeModel();
    model.MyFruits = Fruit.Love | Fruit.Joy | Fruit.Peace | Fruit.Patience;
    return View(model);
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Index(HomeModel returnData)
{
    return View(returnData);
}

如果您检查发送数据的 POST 正文,它只是没有被正确拾取。这是因为 MVC 没有很好地处理标志枚举。有人 who answered 类似的问题是这样描述的:

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.

回答问题的人还提供了关于如何绑定标志枚举的完整答案。您基本上需要做的是创建您自己的可以处理标志枚举的自定义模型绑定器。在另一个名为ASP.Net MVC Flag Enumeration Model Binder的post中我找到了一个示例,我将复制相关代码。

添加一个名为CustomModelBinder的class如下:

public class CustomModelBinder : DefaultModelBinder
{
    protected override object GetPropertyValue(
        ControllerContext controllerContext, 
        ModelBindingContext bindingContext, 
        PropertyDescriptor propertyDescriptor, 
        IModelBinder propertyBinder)
    {
        var propertyType = propertyDescriptor.PropertyType;

        // Check if the property type is an enum with the flag attribute
        if (propertyType.IsEnum && propertyType.GetCustomAttributes(true).Any())
        {
            var providerValue = bindingContext.ValueProvider
                .GetValue(bindingContext.ModelName);
            if (providerValue != null)
            {
                var value = providerValue.RawValue;
                if (value != null)
                {
                    // In case it is a checkbox list/dropdownlist/radio 
                    // button list
                    if (value is string[])
                    {
                        // Create flag value from posted values
                        var flagValue = ((string[])value)
                            .Aggregate(0, (acc, i) 
                                => acc | (int)Enum.Parse(propertyType, i));

                        return Enum.ToObject(propertyType, flagValue);
                    }

                    // In case it is a single value
                    if (value.GetType().IsEnum)
                    {
                        return Enum.ToObject(propertyType, value);
                    }
                }
            }
        }

        return base.GetPropertyValue(controllerContext, 
            bindingContext, 
            propertyDescriptor, 
            propertyBinder);
    }
}

然后在Global.asax.csApplication_Start方法中注册自定义模型绑定器如下:

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterGlobalFilters(GlobalFilters.Filters);
    RegisterRoutes(RouteTable.Routes);

    // Register custom flag enum model binder
    ModelBinders.Binders.DefaultBinder = new CustomModelBinder();
}

这应该有效。

来源:http://itq.nl/asp-net-mvc-flag-enumeration-model-binder/