Blazor에서 Data Validation을 지원하는 InputSelect 컴포넌트는 enum 복수 선택할 때 배열로 처리하기 때문에, flag enum 형식의 속성에 데이터 바인딩을 직접 거는 것이 불가능합니다.
그래서, EditForm에서 간편하게 바인딩할 수 있는 체크박스 컴포넌트를 작성해봤습니다.
// InputFlaggedEnum.razor
@typeparam TEnum
@inject IStringLocalizer<TypeNames> TypeLocalizer
<div class="btn-group" role="group" >
@foreach (var key in _memberInts.Keys)
{
<input type="checkbox" class="btn-check" id="@key.ToString()" autocomplete="off"
@onchange="@(() => OnCheckChanged(key))" checked="@_checkedStataus[key]" />
<label class="btn btn-outline-success" for="@key.ToString()">@TypeLocalizer[key.ToString()]</label>
}
</div>
@code {
[Parameter]
[EditorRequired]
public TEnum? Options { get; set; }
[Parameter]
public TEnum? Value { get; set; }
private int _valueInt;
[Parameter]
public EventCallback<TEnum> ValueChanged { get; set; }
private Dictionary<TEnum, int> _memberInts = new();
private Dictionary<TEnum, bool> _checkedStataus = new();
private async void OnCheckChanged(TEnum selected)
{
_checkedStataus[selected] = !_checkedStataus[selected];
var memberInt = _memberInts[selected];
if (_checkedStataus[selected])
{
Value = StringToEnum( (_valueInt |= memberInt).ToString() );
}
else
{
Value = StringToEnum( (_valueInt &= (_valueInt ^ memberInt)).ToString() );
}
await ValueChanged.InvokeAsync(Value);
}
protected override void OnInitialized()
{
base.OnInitialized();
if(Options != null)
{
var enumType = Options.GetType();
if (enumType.IsEnum)
{
var optionInt = EnumToInt(Options);
foreach (var member in Enum.GetValues(enumType))
{
if (System.Numerics.BitOperations.PopCount((uint)(int)member) != 1)
continue;
var memberNum = (int)member;
if ((memberNum & optionInt) == memberNum)
{
_memberInts[(TEnum)member] = memberNum;
_checkedStataus[(TEnum)member] = false;
}
}
if (Value != null)
{
_valueInt = EnumToInt(Value);
if (_valueInt != 0)
{
foreach (var memberInt in _memberInts)
{
if ((_valueInt & memberInt.Value) == memberInt.Value)
{
_checkedStataus[memberInt.Key] = true;
}
}
}
}
}
}
}
private int EnumToInt(TEnum value)
{
return (int)Enum.Parse(typeof(TEnum), value.ToString());
}
private TEnum StringToEnum(string numOrConstant)
{
return (TEnum)Enum.Parse(typeof(TEnum), numOrConstant);
}
}
[Flags]
public enum DayOfWeekFlag
{
Non = 0, // == defualtOf(DayOfWeekFlag)
Sun = 1 << DayOfWeek.Sunday,
Mon = 1 << DayOfWeek.Monday,
Tue = 1 << DayOfWeek.Tuesday,
Wed = 1 << DayOfWeek.Wednesday,
Thu = 1 << DayOfWeek.Thursday,
Fri = 1 << DayOfWeek.Friday,
Sat = 1 << DayOfWeek.Saturday,
End = 0b100_0001,
All = 0b111_1111,
}
사용하기
// index.razor
@attribute [Route(Routes.Index)]
@inject IStringLocalizer<TypeNames> TypeLocalizer
@code {
private DayOfWeekFlag DayOfWeek { get; set; } = DayOfWeekFlag.End;
}
@{
var options = DayOfWeekFlag.All;
}
<InputFlaggedEnum Options="@options" @bind-Value="@DayOfWeek"/>
<h3>
@string.Join("", DayOfWeek
.ToString()
.Split(", ")
.Select( x => TypeLocalizer[x].ToString())
.ToArray())
</h3>
초기값
체크 변경
옵션 변경
// index.razor
...
@{
var options = DayOfWeekFlag.All ^ DayOfWeekFlag.End;
}
...