Я пытаюсь создать собственный раскрывающийся компонент Blazor для перечислений, который может сортировать элементы перечисления по их отображаемому значению/тексту. У меня есть следующий код:
<div>
<select value = "@this.SelectedValue" @onchange = "@this.OnSelectedValueChanged" @key = "@this.Items">
@foreach (var value in this.Items)
{
// Use enum value as value and display name as text.
if (value is Enum enumName)
{
<option id = "@value" value = "@value">@enumName.GetDisplayName(CultureInfo.CurrentCulture)</option>
}
}
</select>
</div>
перечисление порядка сортировки:
public enum SortOrder
{
None,
Ascending,
Descending
}
и в коде файла:
private List<TItem?> OriginalItems { get; set; } = [];
[Parameter]
[EditorRequired]
public IEnumerable<TItem?> Items { get; set; } = [];
[Parameter]
public TItem? SelectedValue { get; set; }
[Parameter]
public EventCallback<TItem?> SelectedValueChanged { get; set; }
[Parameter]
public EventCallback<TItem?> OnValueChanged { get; set; }
[Parameter]
public SortOrder SortOrder { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
this.OriginalItems = this.Items.ToList();
this.SortItems();
}
protected async Task OnSelectedValueChanged(ChangeEventArgs args)
{
if (args.Value is not string value)
{
return;
}
if (this.SelectedValue is Enum _)
{
try
{
var result = (TItem?)Enum.Parse(typeof(TItem?), value);
await this.SelectedValueChanged.InvokeAsync(result);
await this.OnValueChanged.InvokeAsync(result);
}
catch
{
// ignored
}
}
}
private void SortItems()
{
if (this.SortOrder == SortOrder.None)
{
return;
}
if (typeof(TItem).BaseType == typeof(Enum))
{
this.Items = this.SortOrder == SortOrder.Ascending ?
[.. this.Items.OrderBy(f => (f as Enum)?.GetDisplayNameForEnum(CultureInfo.CurrentCulture))] :
[.. this.Items.OrderByDescending(f => (f as Enum)?.GetDisplayNameForEnum(CultureInfo.CurrentCulture))];
}
}
Функция GetDisplayValue просто получает отображаемое значение для перечислений и реализуется для всех перечислений, помещенных в раскрывающийся список, например:
private static string GetDisplayName(this SortOrder order, CultureInfo culture)
{
var de = culture.Name.StartsWith("de");
return order switch
{
SortOrder.None => de ? "Keine" : "None",
SortOrder.Ascending => de ? "Aufsteigend" : "Ascending",
SortOrder.Descending => de ? "Absteigend" : "Descending",
_ => $"[[{type}]]"
};
}
Существует также поле ввода для фильтрации, метод фильтрации, размер фильтра и несколько других вещей, таких как установленные идентификаторы. Однако я удалил их, поскольку они не имеют отношения к вопросу. TItem является общим членом (и в моем случае используется только для Enums).
Когда я добавляю компонент на страницу, первоначальная сортировка работает. Поиск работает. Однако, когда я выбираю один из элементов, сортировка игнорируется и select, кажется, откатывается назад/"Сортируется" по значению члена перечисления (например, тому, что указано в value= из option). Есть ли способ всегда сортировать select по отображаемому имени даже после изменения выбора значения (после вызова OnSelectedValueChanged())?
Подсказка: вызов SortItems() в конце OnSelectedValueChanged() не работает.
Подсказка 2: вызов SortItems() перед this.OriginalItems = this.Items.ToList(); в OnInitialized() тоже не помогает.
Подсказка 3: использование индекса списка this.Items в качестве значения при выборе после сортировки списка также не работает, плюс имеет понижение версии, что выбранное значение установлено правильно, но больше не отображается в раскрывающемся списке.
@MrCakaShaunCurtis Теоретически да, но в некоторых случаях я хотел исключить некоторых членов перечисления, а в некоторых — нет. Поэтому на родительской странице я могу решить, следует ли использовать все элементы или нет. Ограничение типа TItem: Enum было бы полезно, но почему-то не работает с Blazor и nullables.





Решение, которое я придумал сейчас, заключалось в том, чтобы переписать функцию SortItems, чтобы она возвращала значение, была ли коллекция изменена (например, изменена), а затем использовать OnAfterRender() для вызова StateHasChanged().
Изменения:
protected override void OnAfterRender(bool firstRender)
{
if (!firstRender)
{
if (this.SortItems())
{
this.StateHasChanged();
}
}
base.OnAfterRender(firstRender);
}
private bool SortItems()
{
if (this.SortOrder == SortOrder.None)
{
return false;
}
if (typeof(TItem).BaseType == typeof(Enum))
{
var initialItems = this.Items.ToList();
this.Items = this.SortOrder == SortOrder.Ascending ?
[.. this.Items.OrderBy(f => (f as Enum)?.GetDisplayName(CultureInfo.CurrentCulture))] :
[.. this.Items.OrderByDescending(f => (f as Enum)?.GetDisplayName(CultureInfo.CurrentCulture))];
return !Enumerable.SequenceEqual(initialItems, this.Items);
}
return false;
}
Я в замешательстве и, вероятно, совершенно не понимаю сути. Если вы хотите создать выборку для перечисления, то почему вы передаете
public IEnumerable<TItem?> Items { get; set; } = []? Конечно, вы просто передаете тип перечисления и выполняете итерациюEnum.GetValuesв элементе управления, чтобы получить возможные значения?