2

I have an [Flags] Enum that contains some datapoints that I want to collect. My goal is to display and bind each value(each bit) as an individual checkbox for the user to check. However, I do not know how to bind individual values of an Flags enum to a component, nor if it is even possible. To better explain my point:

Say this is my enum:

[Flags]
public enum DataPoints{
     Large = 1,
     Soft  = 2,
     Green = 4,
     Round = 8
}

Then I'd want a property in my form's model of DataPoints type that can hold those values:

//possible data points like:
// 9: Round and Large
// 6: Soft and Green
public DataPoints Data;

I was thinking something like the following but I don't think you can bind individual values/bits of an enum to a component like that:

@foreach (DataPoints datum in Enum.GetValues(typeof(DataPoints)))
   {
      <CheckBox @bind-Value="what? Data.datum?"></CheckBox>
   }
Bubinga
  • 613
  • 12
  • 29
  • https://stackoverflow.com/questions/51234654/net-core-blazor-how-to-get-the-checkbox-value-if-it-is-checked – vernou Mar 29 '21 at 09:53
  • @vernou that question doesn't appear helpful. I know how to bind a single checkbox to a single bool property, I'm asking how I bind multiple checkboxes to an enum property – Bubinga Mar 29 '21 at 10:16
  • Hang on a few minutes, I'll have an answer for you pretty soon, Bubinga. – Bennyboy1973 Mar 29 '21 at 10:28

2 Answers2

6

Something I find myself doing a LOT in Blazor is making what I call carrier classes, especially for selecting items. I use the same technique to select images from a list, answers from a multiple choice question, and so on.

@foreach (var item in CheckableEnums)
{
    <span> @item.DataPoint.ToString() &nbsp;</span>
    <input type="checkbox" @bind="item.IsSelected" />
    <br />
}
<br />
@{ SumValue = (DataPoints)(CheckableEnums.Where(cb => cb.IsSelected).Select(val => (int)val.DataPoint)).Sum(); }
<span>Selected Flags: </span>@SumValue.ToString()

@code {
    [Flags]
    public enum DataPoints
    {
        Large = 1,
        Soft = 2,
        Green = 4,
        Round = 8
    }
    class CheckableEnum
    {
        public DataPoints DataPoint { get; set; }
        public bool IsSelected;
    }
    DataPoints SumValue { get; set; }
    List<CheckableEnum> CheckableEnums { get; set; } = new List<CheckableEnum>();
    protected override void OnInitialized()
    {
        foreach (var DPvalue in Enum.GetValues(typeof(DataPoints)))
            CheckableEnums.Add(new CheckableEnum() { DataPoint = (DataPoints)DPvalue });
    }
}

-edit- The previous fiddle was broken so: https://blazorfiddle.com/s/t523591k

Bennyboy1973
  • 3,413
  • 2
  • 11
  • 16
  • Nice, but not complete. You're basically making a view model. Still needs a method to combine it back to a single object (ORing all the items in the list). (I would personally use `Enum.GetValues(typeof(DataPoints)).ToArray()` as you will not mutate it) – JHBonarius Mar 29 '21 at 11:48
  • OK, I edited the sample to combine the flags into a single object, `SumValue`. The `.Select()` converts the flags to `int`, so you can just add them with `.Sum()`, then the sum is cast back to the `DataPoints` type. The `.ToString()` method shows all the selected flags, as expected. I call this a "carrier class" because I use it to conflate data and state, rather than for Validation or anything like that. – Bennyboy1973 Mar 29 '21 at 12:43
  • Yeah, sum works in this case.... in case you have a situation where one enum value could occur twice it wouldn't. 4 OR 4 = 4. 4+4=8 – JHBonarius Mar 29 '21 at 15:50
  • Yes, that's right. Also, if any of the flags were combination values ("SoftGreen = 6") it wouldn't work either. I'm not sure about the most efficient way of OR-ing an Enumerable collection. – Bennyboy1973 Mar 30 '21 at 05:59
  • It's probably LINQ `Aggregate`: `.Aggregate(0,(result, value) => result | value);` – JHBonarius Mar 30 '21 at 08:17
2

Since my first answer is already accepted, I didn't want to change it. But JHBonarius' suggestion to use .Aggregate() is better if there are any combination Flags:

In markup:

@{ SumValue = CheckableEnums.Where(cb => cb.IsSelected).Select(val => val.DataPoint).Aggregate((DataPoints)0,(result, value) => result | value); }

and modified test flags:

@code {
    [Flags]
    public enum DataPoints
    {
        Large = 1,
        Soft = 2,
        Green = 4,
        Round = 8,

        LargeAndSoft = 3 // Added value.
    }
}

Fiddle: https://blazorfiddle.com/s/hu9kooon

Bennyboy1973
  • 3,413
  • 2
  • 11
  • 16