简体   繁体   English

使用带有标志的PostBack枚举复选框

[英]Using checkboxes to PostBack Enum with Flags

I have an enum property and I am trying to set its value through checkboxes. 我有一个枚举属性,我试图通过复选框设置其值。 The enum is flagged and when the user selects multiple options I expect the property to have all the selected flags concatenated. 枚举枚举,当用户选择多个选项时,我希望该属性连接所有选定的标志。

I tried adding a checkbox for each enum value and gave every checkbox the same name. 我尝试为每个枚举值添加一个复选框,并为每个复选框指定相同的名称。 During postback the first selected flag is retrieved but not concatenated with the other flags. 在回发期间,检索第一个选定的标志但不与其他标志连接。

Could I fix this somehow without having separate property for each flag? 如果不为每个标志分别拥有单独的属性,我可以以某种方式修复

Model 模型

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
}

View 视图

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

Controller 调节器

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

If you check the POST body the data is sent, it is just not picked up properly. 如果您检查POST主体是否已发送数据,则无法正确选取数据。 This is because MVC is not handling flags enumerations well. 这是因为MVC没有很好地处理标志枚举。 Someone who answered a similar question describes this: 回答类似问题的人描述了这个:

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. 一般来说,我在设计视图模型时避免使用枚举,因为它们不能使用ASP.NET MVC的帮助程序和开箱即用的模型绑定器。 They are perfectly fine in your domain models but for view models you could use other types. 它们在您的域模型中非常精细,但对于视图模型,您可以使用其他类型。

There the person answering the question also provides a full answer to how to bind a flags enumeration anyway. 在那里,回答问题的人还提供了如何绑定标志枚举的完整答案。 What you basically need to do is create your own custom model binder that can handle flags enumeration. 你基本上需要做的是创建自己的自定义模型绑定器,它可以处理标志枚举。 In another post called ASP.Net MVC Flag Enumeration Model Binder I found an example and I will copy the relevant code. 在另一篇名为ASP.Net MVC Flag Enumeration Model Binder的文章中,我找到了一个例子,我将复制相关代码。

Add a class called CustomModelBinder as follows: 添加一个名为CustomModelBinder的类,如下所示:

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

Then in the Global.asax.cs Application_Start method register the custom model binder as follows: 然后在Global.asax.cs Application_Start方法中注册自定义模型绑定器,如下所示:

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

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

This should work. 这应该工作。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM