1

I have a EnumDropDownListFor in my ASP.Net MVC site that represents a list of times (ex. 5:00AM, 5:15AM, 5:30AM through 9:00PM in 15 min increments) that are coming from an enum

public enum YogaTime
{
    [Display(Name = "5:00 AM")]
    Five,
    [Display(Name = "5:15 AM")]
    FiveFifteen,
    [Display(Name = "5:30 AM")]
    FiveThirty,
    [Display(Name = "5:45 AM")]
    FiveFortyFive,
    [Display(Name = "6:00 AM")]
    Six,
    ...
    ...
    ...
    [Display(Name = "9:00 PM")]
    Nine,
}

I display all the times like this in the view

<div class="col-sm-6" style="padding-left: 0px; padding-right: 0px;">
    @Html.EnumDropDownListFor(m => m.StartTime, new { @class = "form-control selectpicker", @name = "StartTime" })
</div>

is there an easy way to remove or disable all the times that are before the current time? ex. if it's 2:00PM remove or disable all times before 2:00PM

FYI - I'm using bootstrap selectpicker to style the dropdown.

chuckd
  • 13,460
  • 29
  • 152
  • 331
  • Not related, but your `new { @name = "StartTime" }` is unnecessary (the method already adds the correct `name` attribute, and its ignored internally anyway) –  Jan 18 '18 at 00:27

3 Answers3

0

The basic idea is to assign YogaTime an time related integer value. I use total minutes passed from the beginning of the day as each YogaTime's value (Hour * 60 + Minute).

public enum YogaTime
{
    [Display(Name = "5:00 AM")]
    Five = 300,                // 5 * 60 + 0
    [Display(Name = "5:15 AM")]
    FiveFifteen = 315,         // 5 * 60 + 15
    [Display(Name = "5:30 AM")]
    FiveThirty = 330,          // 5 * 60 + 30
    [Display(Name = "5:45 AM")]
    FiveFortyFive = 345,       // 5 * 60 + 45
    [Display(Name = "6:00 AM")]
    Six = 360,                 // 6 * 60 + 0
    [Display(Name = "7:00 PM")]
    Seven = 1140,              // 19 * 60 + 0
    [Display(Name = "9:00 PM")]
    Nine = 1260,               // 21 * 60 + 0
}

Then you could use DropDownListFor to filter out those past time slots, which also could be done in Controller method and pass the result through ViewBag.

@using System.ComponentModel.DataAnnotations
@using WebApplication1.Models
@model YogaViewModel
@{
    var now = DateTime.Now;
    var yogaTimes = Enum.GetValues(typeof(YogaTime)).Cast<int>().Where(t => t > now.Hour * 60 + now.Minute).Cast<YogaTime>();
    var selectList = new List<SelectListItem>();
    foreach (var yogaTime in yogaTimes)
    {
        var memberInfo = typeof(YogaTime).GetMember(yogaTime.ToString());
        if (memberInfo.Length == 0) { continue; }
        var attributes = memberInfo[0].GetCustomAttributes(typeof(DisplayAttribute), false);
        if (attributes.Length == 0) { continue; }
        var displayAttribute = attributes[0] as DisplayAttribute;
        if (displayAttribute == null) { continue; }
        selectList.Add(new SelectListItem
        {
            Value = ((int)yogaTime).ToString(),
            Text = displayAttribute.Name
        });
    }
}

<div class="col-sm-6" style="padding-left: 0px; padding-right: 0px;">
    @Html.DropDownListFor(m => m.StartTime, selectList, new { @class = "form-control selectpicker", @name = "StartTime" })
</div>
Han Zhao
  • 1,932
  • 7
  • 10
0

Assume you have enum with customized values like this (I used 24-hour format similar to military time):

public enum YogaTime
{
    [Display(Name = "12:00 AM")]
    Zero = 0,
    [Display(Name = "12:15 AM")]
    ZeroFifteen = 15,
    [Display(Name = "12:30 AM")]
    ZeroThirty = 30,
    [Display(Name = "12:45 AM")]
    ZeroFortyFive = 45,
    [Display(Name = "1:00 AM")]
    One = 100,

    // other values

    [Display(Name = "9:00 PM")]
    TwentyOne = 2100,
}

The solution based from similar issue here, you need to do steps like this:

1) Create GetDisplayName method to display enum values.

// taken from /a/32193206
public static string GetDisplayName(this Enum enum)
{
    Type enumType = enumeration.GetType();
    string enumName = Enum.GetName(enumType, enumeration);
    string displayName = enumName;
    try
    {
        MemberInfo member = enumType.GetMember(enumName)[0];

        object[] attributes = member.GetCustomAttributes(typeof(DisplayAttribute), false);
        DisplayAttribute attribute = (DisplayAttribute)attributes[0];
        displayName = attribute.Name;

        if (attribute.ResourceType != null)
        {
            displayName = attribute.GetName();
        }
    }
    catch (Exception e) { }
    return displayName;
}

2) Create viewmodel property which declared as List<SelectListItem> to hold dropdownlist values e.g. EnumSelectList.

3) Put desired condition in controller action method as given in this example:

var model = new ViewModel();

// cast to proper enum values with current hour & minute
YogaTime hour = (YogaTime)((DateTime.Now.Hour * 100) + DateTime.Now.Minute);

model.EnumSelectList = Enum.GetValues(typeof(YogaTime))
               .Cast<YogaTime>().Where(x => x >= hour)
               .Select(x => new SelectListItem() 
               {
                    Value = ((int)x).ToString(),
                    Text = ((Enum)(object)x).GetDisplayName()
               }).ToList();

return View(model);

4) In the view, EnumDropDownListFor should be changed to DropDownListFor because enum values are hardcoded at compile time.

@Html.DropDownListFor(model => model.StartTime, Model.EnumSelectList, new { @class = "form-control selectpicker" })

Check for live example: .NET Fiddle

Tetsuya Yamamoto
  • 24,297
  • 8
  • 39
  • 61
0

1º: Change your method to DropDownListFor, the signature has an extra parameter for the SelectList, it is the possible values to show on control.

2º: Build the select list using this extension method on enum:

 public static SelectList ToSelectList<TEnum>(this TEnum enumObj,
                                                      Func<TEnum, string> getText = null,
                                                      Func<TEnum, object> getValue = null,
                                                      Func<TEnum, bool> ignore = null)
    {
        var query = Enum.GetValues(typeof(TEnum)).Cast<TEnum>();

        if (ignore != null)
            query = query.Where(e => !ignore(e));

        var values = query.Select(e =>
        {   
            object value = getValue != null ? getValue(e) : e;

            string name = null;

            if (getText != null)
                name = getText(e);

            name = name ?? EnumExtension.GetDisplayName((Enum)(object) e) ?? e.ToString();

            return new { @Name = name, @Value = value };
        });

        return new SelectList(values, "Value", "Name", enumObj);
    }

3º: The last optional parameter let you write an filter to ignore these values.

4º: You have to get Display Attribute from the EnumValue, use an static method like these: How to get the Display Name Attribute of an Enum member via MVC razor code?.

5º: For the Last, you have to parse string of display to DateTime and compare it to DateTime.UtcNow.

So the code (dense form, can be refactored and change use of Parse to TryParse) should be like that:

@Html.DropDownListFor(m => m.StartTime,
                      YogaTime.ToSelectList(t =>         
                          DateTime.Parse(t.GetDisplayName()) <= 
                          DateTime.UtcNow)),
                      new { @class = "form-control selectpicker" })

Extra: It's the way that you want, now I would like to suggest to don't build this at the .cshtml

You can do the same thing, but building the SelectList on the Controller and using the ViewBag, like that:

 @Html.DropDownListFor(m => m.StartTime,
                      (SelectList)ViewBag.Times,
                      new { @class = "form-control selectpicker" })

Where ViewBag is the ExpandoObject for dynamic operations on view and can be build using the same methods that I mentioned or explicited before.

Hope it Helps!

Daniel Cruz
  • 54
  • 1
  • 5