diff --git a/src/BootstrapBlazor.Server/Components/Samples/MultiSelects.razor b/src/BootstrapBlazor.Server/Components/Samples/MultiSelects.razor
index 6b1ed9b4114..b60402b6777 100644
--- a/src/BootstrapBlazor.Server/Components/Samples/MultiSelects.razor
+++ b/src/BootstrapBlazor.Server/Components/Samples/MultiSelects.razor
@@ -5,31 +5,31 @@
@Localizer["MultiSelectsDescription"]
-
+@*
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -40,7 +40,7 @@
@@ -51,12 +51,12 @@
@@ -66,12 +66,12 @@
@((MarkupString)Localizer["MultiSelectBindingCollectionDescription"].Value)
@(string.Join(",", SelectedArrayValues))
@@ -84,9 +84,9 @@
-
-
-
+
+
+
@(string.Join(",", SelectedIntArrayValues))
@@ -94,11 +94,26 @@
@((MarkupString)Localizer["MultiSelectBindingEnumCollectionDescription"].Value)
-
+
@(string.Join(",", SelectedEnumValues))
+ *@
+
+
+
+ [Flags]
+private enum MultiSelectEnumFoo
+{
+ One = 1,
+ Two = 2,
+ Three = 4,
+ Four = 8
+}
+
+
-
+@*
@((MarkupString)Localizer["MultiSelectSearchDescription"].Value)
@SelectedSearchItemsValue
@@ -192,7 +207,7 @@
@((MarkupString)Localizer["MultiSelectCascadingDescription"].Value)
-
+ *@
diff --git a/src/BootstrapBlazor.Server/Components/Samples/MultiSelects.razor.cs b/src/BootstrapBlazor.Server/Components/Samples/MultiSelects.razor.cs
index e7a568e1842..3c0d43256df 100644
--- a/src/BootstrapBlazor.Server/Components/Samples/MultiSelects.razor.cs
+++ b/src/BootstrapBlazor.Server/Components/Samples/MultiSelects.razor.cs
@@ -62,13 +62,24 @@ public partial class MultiSelects
private string SelectedItemsValue { get; set; } = "Beijing";
- private IEnumerable SelectedArrayValues { get; set; } = Enumerable.Empty();
+ private IEnumerable SelectedArrayValues { get; set; } = [];
private IEnumerable SelectedEnumValues { get; set; } = new List
{
EnumEducation.Middle, EnumEducation.Primary
};
+ private MultiSelectEnumFoo EnumFoo { get; set; } = MultiSelectEnumFoo.One | MultiSelectEnumFoo.Two;
+
+ [Flags]
+ private enum MultiSelectEnumFoo
+ {
+ One = 1,
+ Two = 2,
+ Three = 4,
+ Four = 8
+ }
+
[NotNull]
private ConsoleLogger? Logger { get; set; }
@@ -101,7 +112,7 @@ private async Task OnEditCallback(string value)
{
await Task.Delay(100);
- var item = EditableItems.Find(i => i.Text.Equals(value, System.StringComparison.OrdinalIgnoreCase));
+ var item = EditableItems.Find(i => i.Text.Equals(value, StringComparison.OrdinalIgnoreCase));
if (item == null)
{
item = new SelectedItem(value, value);
@@ -120,7 +131,7 @@ private async Task OnEditCallback(string value)
new("Ningbo", "宁波") {GroupName = "华东", Active = true }
];
- private readonly SelectedItem[] CascadingItems2 =
+ private readonly SelectedItem[] _cascadingItems2 =
[
new("", "请选择 ..."),
new("Beijing", "北京") { Active = true },
@@ -209,12 +220,12 @@ private void AddListItems()
private void RemoveListItems()
{
- SelectedArrayValues = new[] { "Beijing" };
+ SelectedArrayValues = ["Beijing"];
}
private void ClearListItems()
{
- SelectedArrayValues = Enumerable.Empty();
+ SelectedArrayValues = [];
}
private void AddArrayItems()
@@ -236,7 +247,7 @@ private IEnumerable OnSearch(string searchText)
{
Logger.Log($"{Localizer["MultiSelectSearchLog"]}:{searchText}");
SearchItemsSource ??= GenerateItems();
- return SearchItemsSource.Where(i => i.Text.Contains(searchText, System.StringComparison.OrdinalIgnoreCase));
+ return SearchItemsSource.Where(i => i.Text.Contains(searchText, StringComparison.OrdinalIgnoreCase));
}
private Task OnSelectedItemsChanged8(IEnumerable items)
diff --git a/src/BootstrapBlazor.Server/Locales/en-US.json b/src/BootstrapBlazor.Server/Locales/en-US.json
index 10a17becbf1..606ae70c63f 100644
--- a/src/BootstrapBlazor.Server/Locales/en-US.json
+++ b/src/BootstrapBlazor.Server/Locales/en-US.json
@@ -2941,6 +2941,8 @@
"MultiSelectSearchTitle": "Search function",
"MultiSelectSearchIntro": "Turn on search by setting the ShowSearch
value",
"MultiSelectSearchDescription": "In this example, the search callback delegate method is set onSearchTextChanged
to customize search results if the display text is used internally to make a fuzzy match when not set",
+ "MultiSelectFlagsEnumTitle": "Flags Enum",
+ "MultiSelectFlagsEnumIntro": "When the binding value is an Enum
data type, if it has a Flags
tag, multiple selection mode is automatically supported",
"MultiSelectGroupTitle": "Grouping",
"MultiSelectGroupIntro": "Alternatives are presented in groups",
"MultiSelectDisableTitle": "Disable the feature",
diff --git a/src/BootstrapBlazor.Server/Locales/zh-CN.json b/src/BootstrapBlazor.Server/Locales/zh-CN.json
index c7bb88fe88a..597e60ff85f 100644
--- a/src/BootstrapBlazor.Server/Locales/zh-CN.json
+++ b/src/BootstrapBlazor.Server/Locales/zh-CN.json
@@ -2941,6 +2941,8 @@
"MultiSelectSearchTitle": "搜索功能",
"MultiSelectSearchIntro": "通过设置 ShowSearch
值开启搜索功能",
"MultiSelectSearchDescription": "本例中设置搜索回调委托方法 OnSearchTextChanged
进行自定义搜索结果,如果未设置时内部使用显示文本进行模糊匹配",
+ "MultiSelectFlagsEnumTitle": "Flags 枚举",
+ "MultiSelectFlagsEnumIntro": "绑定值为 Enum
数据类型时,如果枚举有 Flags
标签时,自动支持多选模式",
"MultiSelectGroupTitle": "分组",
"MultiSelectGroupIntro": "通过设置 GroupName
将下拉框中的备选项进行分组显示",
"MultiSelectDisableTitle": "禁用功能",
diff --git a/src/BootstrapBlazor/Components/Select/MultiSelect.razor.cs b/src/BootstrapBlazor/Components/Select/MultiSelect.razor.cs
index cdea5f0faba..a225524efa9 100644
--- a/src/BootstrapBlazor/Components/Select/MultiSelect.razor.cs
+++ b/src/BootstrapBlazor/Components/Select/MultiSelect.razor.cs
@@ -5,6 +5,8 @@
using Microsoft.Extensions.Localization;
using System.Collections;
+using System.Collections.Specialized;
+using System.Reflection;
namespace BootstrapBlazor.Components;
@@ -234,13 +236,15 @@ protected override void OnParametersSet()
ResetRules();
_itemsCache = null;
+
// 通过 Value 对集合进行赋值
- if (PreviousValue != CurrentValueAsString)
+ var _currentValue = CurrentValueAsString;
+ if (PreviousValue != _currentValue)
{
- PreviousValue = CurrentValueAsString;
- var list = CurrentValueAsString.Split(',', StringSplitOptions.RemoveEmptyEntries);
+ PreviousValue = _currentValue;
+ var list = _currentValue.Split(',', StringSplitOptions.RemoveEmptyEntries);
SelectedItems.Clear();
- SelectedItems.AddRange(Rows.Where(item => list.Any(i => i == item.Value)));
+ SelectedItems.AddRange(Rows.Where(item => list.Any(i => i.Trim() == item.Value)));
}
}
@@ -397,14 +401,13 @@ private void ResetRules()
private async Task SetValue()
{
- var typeValue = NullableUnderlyingType ?? typeof(TValue);
- if (typeValue == typeof(string))
+ if (ValueType == typeof(string))
{
CurrentValueAsString = string.Join(",", SelectedItems.Select(i => i.Value));
}
- else if (typeValue.IsGenericType || typeValue.IsArray)
+ else if (ValueType.IsGenericType || ValueType.IsArray)
{
- var t = typeValue.IsGenericType ? typeValue.GenericTypeArguments[0] : typeValue.GetElementType()!;
+ var t = ValueType.IsGenericType ? ValueType.GenericTypeArguments[0] : ValueType.GetElementType()!;
var listType = typeof(List<>).MakeGenericType(t);
var instance = (IList)Activator.CreateInstance(listType, SelectedItems.Count)!;
@@ -415,7 +418,11 @@ private async Task SetValue()
instance.Add(val);
}
}
- CurrentValue = (TValue)(typeValue.IsGenericType ? instance : listType.GetMethod("ToArray")!.Invoke(instance, null)!);
+ CurrentValue = (TValue)(ValueType.IsGenericType ? instance : listType.GetMethod("ToArray")!.Invoke(instance, null)!);
+ }
+ else if (ValueType.IsFlagEnum())
+ {
+ CurrentValue = (TValue?)SelectedItems.ParseFlagEnum(ValueType);
}
if (ValidateForm == null && (Min > 0 || Max > 0))
diff --git a/src/BootstrapBlazor/Extensions/EnumExtensions.cs b/src/BootstrapBlazor/Extensions/EnumExtensions.cs
index ffa64d18efb..127f4269a11 100644
--- a/src/BootstrapBlazor/Extensions/EnumExtensions.cs
+++ b/src/BootstrapBlazor/Extensions/EnumExtensions.cs
@@ -63,11 +63,7 @@ public static List ToSelectList(this Type type, SelectedItem? addi
if (type.IsEnum())
{
var t = Nullable.GetUnderlyingType(type) ?? type;
- foreach (var field in Enum.GetNames(t))
- {
- var desc = Utility.GetDisplayName(t, field);
- ret.Add(new SelectedItem(field, desc));
- }
+ ret.AddRange(from field in Enum.GetNames(t) let desc = Utility.GetDisplayName(t, field) select new SelectedItem(field, desc));
}
return ret;
}
@@ -114,4 +110,33 @@ public static bool IsEnum(this Type? type)
}
return ret;
}
+
+ ///
+ /// 判断类型是否为 Flag 枚举类型
+ ///
+ ///
+ ///
+ public static bool IsFlagEnum(this Type? type) => type != null && IsEnum(type) && type.GetCustomAttribute() != null;
+
+ ///
+ /// 将 集合转换为 Flag 枚举值
+ ///
+ ///
+ ///
+ ///
+ internal static object? ParseFlagEnum(this IEnumerable items, Type type)
+ {
+ TValue? v = default;
+ if (type.IsFlagEnum())
+ {
+ foreach (var item in items)
+ {
+ if (Enum.TryParse(type, item.Value, true, out var val))
+ {
+ v = (TValue)Enum.ToObject(type, Convert.ToInt32(v) | Convert.ToInt32(val));
+ }
+ }
+ }
+ return v;
+ }
}
diff --git a/src/BootstrapBlazor/Utils/Utility.cs b/src/BootstrapBlazor/Utils/Utility.cs
index 20d61864124..74f5ff6285c 100644
--- a/src/BootstrapBlazor/Utils/Utility.cs
+++ b/src/BootstrapBlazor/Utils/Utility.cs
@@ -830,6 +830,10 @@ public static string Format(object? source, IFormatProvider provider)
}
}
}
+ else if (typeValue.IsFlagEnum())
+ {
+ ret = value!.ToString();
+ }
return ret;
}
diff --git a/test/UnitTest/Components/MultiSelectTest.cs b/test/UnitTest/Components/MultiSelectTest.cs
index 34b732c0635..6e9e5a8455b 100644
--- a/test/UnitTest/Components/MultiSelectTest.cs
+++ b/test/UnitTest/Components/MultiSelectTest.cs
@@ -153,6 +153,33 @@ public void EnumValue_Ok()
Assert.Contains("multi-select", cut.Markup);
}
+ [Fact]
+ public async Task FlagEnum_Ok()
+ {
+ var value = MockFlagEnum.One | MockFlagEnum.Two;
+ var cut = Context.RenderComponent>(pb =>
+ {
+ pb.Add(a => a.Value, value);
+ });
+ var values = cut.FindAll(".multi-select-items .multi-select-item");
+ Assert.Equal(2, values.Count);
+
+ // 选中第四个
+ var item = cut.FindAll(".dropdown-menu .dropdown-item").Last();
+ await cut.InvokeAsync(() => item.Click());
+ values = cut.FindAll(".multi-select-items .multi-select-item");
+ Assert.Equal(3, values.Count);
+ }
+
+ [Flags]
+ private enum MockFlagEnum
+ {
+ One = 1,
+ Two = 2,
+ Three = 4,
+ Four = 8
+ }
+
[Fact]
public void Group_Ok()
{