using System.ComponentModel; using Avalonia; using Avalonia.Controls.Primitives; using Avalonia.Data; namespace Ursa.Controls; public class EnumItemTuple { public string? DisplayName { get; set; } public object? Value { get; set; } } public class EnumSelector: TemplatedControl { public static readonly StyledProperty EnumTypeProperty = AvaloniaProperty.Register( nameof(EnumType), validate: OnTypeValidate); public Type? EnumType { get => GetValue(EnumTypeProperty); set => SetValue(EnumTypeProperty, value); } private static bool OnTypeValidate(Type? arg) { if (arg is null) return true; return arg.IsEnum; } public static readonly StyledProperty ValueProperty = AvaloniaProperty.Register( nameof(Value), defaultBindingMode: BindingMode.TwoWay, coerce:OnValueCoerce); private static object? OnValueCoerce(AvaloniaObject o, object? value) { if (o is not EnumSelector selector) return null; if (value is null) return null; if (value.GetType() != selector.EnumType) return null; var first = selector.Values?.FirstOrDefault(a => Equals(a.Value, value)); if (first is null) return null; return value; } public object? Value { get => GetValue(ValueProperty); set => SetValue(ValueProperty, value); } private EnumItemTuple? _selectedValue; public static readonly DirectProperty SelectedValueProperty = AvaloniaProperty.RegisterDirect( nameof(SelectedValue), o => o.SelectedValue, (o, v) => o.SelectedValue = v); public EnumItemTuple? SelectedValue { get => _selectedValue; private set => SetAndRaise(SelectedValueProperty, ref _selectedValue, value); } public static readonly DirectProperty?> ValuesProperty = AvaloniaProperty.RegisterDirect?>( nameof(Values), o => o.Values); private IList? _values; public IList? Values { get => _values; private set => SetAndRaise(ValuesProperty, ref _values, value); } public static readonly StyledProperty DisplayDescriptionProperty = AvaloniaProperty.Register( nameof(DisplayDescription)); public bool DisplayDescription { get => GetValue(DisplayDescriptionProperty); set => SetValue(DisplayDescriptionProperty, value); } static EnumSelector() { EnumTypeProperty.Changed.AddClassHandler((o, e) => o.OnTypeChanged(e)); SelectedValueProperty.Changed.AddClassHandler((o, e) => o.OnSelectedValueChanged(e)); ValueProperty.Changed.AddClassHandler((o, e) => o.OnValueChanged(e)); } private void OnValueChanged(AvaloniaPropertyChangedEventArgs args) { if (_updateFromComboBox) return; var newValue = args.NewValue.Value; if (newValue is null) { if (Values?.Any() == true) { SetCurrentValue(SelectedValueProperty, null); } } else { if (newValue.GetType() != EnumType) { SetCurrentValue(SelectedValueProperty, null); } var tuple = Values?.FirstOrDefault(x => Equals(x.Value, newValue)); SetCurrentValue(SelectedValueProperty, tuple); } } private bool _updateFromComboBox; private void OnSelectedValueChanged(AvaloniaPropertyChangedEventArgs args) { _updateFromComboBox = true; var newValue = args.NewValue.Value; SetCurrentValue(ValueProperty, newValue?.Value); _updateFromComboBox = false; } private void OnTypeChanged(AvaloniaPropertyChangedEventArgs args) { Values?.Clear(); var newType = args.GetNewValue(); if (newType is null || !newType.IsEnum) { return; } Values = GenerateItemTuple(); } // netstandard 2.0 does not support Enum.GetValuesAsUnderlyingType, which is used for native aot compilation #if NET8_0_OR_GREATER private List GenerateItemTuple() { if (EnumType is null) return new List(); var values = Enum.GetValuesAsUnderlyingType(EnumType); List list = new(); foreach (var value in values) { // value is underlying type like int/byte/short var enumValue = Enum.ToObject(EnumType, value); var displayName = Enum.GetName(EnumType, value); if(displayName is null) continue; var field = EnumType.GetField(displayName); var description = field?.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault(); if (description is not null) { displayName = ((DescriptionAttribute) description).Description; } list.Add(new EnumItemTuple() { DisplayName = displayName, Value = enumValue }); } return list; } #else private List GenerateItemTuple() { if (EnumType is null) return new List(); var values = Enum.GetValues(EnumType); List list = new(); foreach (var value in values) { if (value.GetType() == EnumType) { var displayName = value.ToString(); if(displayName is null) continue; var field = EnumType.GetField(displayName); var description = field?.GetCustomAttributes(typeof(DescriptionAttribute), false).FirstOrDefault(); if (description is not null) { displayName = ((DescriptionAttribute) description).Description; } list.Add(new EnumItemTuple() { DisplayName = displayName, Value = value }); } } return list; } #endif }