An Enumeration-based Dropdown for WASM Blazor

This is the enumeration of the Sons of Israel    וּבְנֵ֣י יִשְׂרָאֵ֣ל לְֽמִסְפָּרָ֡ם

I found the above reference to enumeration in Chronicals 27:1 that seemed appropriate. The Hebrew word (l'misparam) here comes from the root ספר which is a bit unusual in that it has a couple of different meanings. In this instance, it relates to counting, but in other contexts it relates to writing.

I've often used dropdown controls that derive their dropdown items from a collection of values taken from a database. For some dropdowns where only a few items are needed whose values don't frequently change it may be simpler to use a dropdown that uses an enumeration as a data source.

I've created a sample enumeration called Desserts shown below. I'll elaborate on the use of the Display attribute momentarily.

  
   public enum Dessert
    {
        [Display(Name ="Chocolate Cake")]
        ChocolateCake,
        Baklavah,
        [Display(Name = "Fruit Compote")]
        FruitCompote,
        Tiramisu,
        [Display(Name = "Toffee Squares")]
        ToffeeSquares
    }
  
  

The Razor code seen here is very similar to the code I used for my generic dropdown . I make use of the Enum.GetValues() method to iterate over the various values found in my Desserts enumeration.


@typeparam TEnum
<div class="dropdown" @onfocusout="OnFocusOut">
    <button class="btn btn-primary dropdown-toggle @(Show ? "show" : string.Empty)" data-toggle="dropdown" type="button" @onmousedown="OnLabelMouseDown" aria-haspopup="true" aria-expanded="false">
        @Label
    </button>
    <div class="dropdown-menu @(Show ? "show" : string.Empty)">
        @{
            foreach (var enumValue in Enum.GetValues(typeof(TEnum)).Cast<TEnum>())
            {
                <button class="dropdown-item" type="button" @onmousedown="(() => OnItemMouseDown(enumValue))">@enumValue.DisplayName()</button>
            }
        }
    </div>
</div>

  

The C# code-behind is seen below:

  
public partial class EnumDropDown<TEnum> where TEnum : struct, Enum?
    {
        [Parameter]
        public EventCallback<TEnum?> Callback { get; set; }
        [Parameter]
        public TEnum? Selected { get; set; } = null;
        [Parameter]
        public string DefaultLabel { get; set; } = "Select";
        public string? Label => Selected == null ? DefaultLabel : Selected.DisplayName();
        private bool Show { get; set; } = false;

        private void OnFocusOut()
        {
            Show = false;
            StateHasChanged();
        }

        public void OnLabelMouseDown()
        {
            Show = !Show;
        }

        public async Task OnItemMouseDown(TEnum? item)
        {
            Show = false;
            if (item != null)
            {
                if (Callback.HasDelegate)
                {
                    await Callback.InvokeAsync(item);
                }
            }
        }
    }

The point of using the Display attribute from the System.ComponentModel.DataAnnotations namespace is to provide a mechanism for displaying spaces and reordering items in the dropdown. C# doesn't allow you to put spaces in the enum member names, so using the Display attribute is a simple workaround for this limitation. As you can see, at least 3 of the member names require spaces. You'll see that in both the Razor and C# code-behind for this component I employ an extension method called DisplayName() (see below). This method checks the selected member of the enumeration for the presence of a Display attribute. If the attribute is present and it has a Name parameter, that parameter value is returned instead of the ToString() value of the enumeration member. This trick allows us to display member names with spaces (and possibly other characters) that can't normally be used in the Enum definition.

  
public static class EnumExtensions
    {
        public static string? DisplayName(this Enum value)
        {
            if (value == null)
            {
                return null;
            }
            // Read the Display attribute name
            var name = value.ToString();
            if (name != null)
            {
                var member = value.GetType().GetMember(name)[0];
                var displayAttribute = member.GetCustomAttribute<DisplayAttribute>();
                if (displayAttribute != null)
                {
                    return displayAttribute.GetName();
                }
            }
            return Enum.GetName(value.GetType(), value);
        }
    }  
  
  

You can watch a short video demonstrating the use of the dropdown below.

The source code shown in this posting and a simple demo Razor page utilizing the dropdown can be found in my Github repository.

No comments:

Post a Comment