如何在不重复 C# 代码的情况下在 ViewModel 上创建可变长度下拉列表?

How do I create a variable length dropdown list on a ViewModel without repeating code in c#?

我是新手开发人员,我需要用日期填充下拉列表,以便网站访问者可以选择它们来检索某些数据。

该列表不寻常,因为它必须从上个月到一年前向后填充。因此,上个月将是列表中的第一个条目,然后是之前的每个月。例如,如果今天是 2017 年 7 月 5 日,则下拉列表将如下所示:

Jun 2017
May 2017
Apr 2017
Mar 2017
Feb 2017
Jan 2017
Dec 2016
and so on . . .

列表每个月都会变长,所以我无法为列表编写固定长度的代码。我想出了以下代码,但它无法构建:

[RequireLogin]
public ActionResult Index()
{
    bool firstItem = true;
    int x = -1;
    int y = 1;
    int daysFromStartDate = beginDate.Date.Subtract(currentDate).Days;
    List<SelectListItem> items = new List<SelectListItem>();

    while (daysFromStartDate >= 30)
    {
        DateTime previousMonth = DateTime.Now.Date.AddMonths(x);
        string pMonth = previousMonth.ToString("MMM");
        string pYear = previousMonth.ToString("YYYY");
        Int32.TryParse(pMonth, out thisMonth);
        Int32.TryParse(pYear, out thisYear);
        if (firstItem)
            firstItem = false;
            SelectListItem item[y] = new SelectListItem() { Text = pMonth + " " + pYear, Value = pMonth, Selected = true };  //<- #1
        else
            SelectListItem item[y]= new SelectListItem() { Text = pMonth + " " + pYear, Value = pMonth, Selected = false };  //<- #2
        items.Add(item[y]);                                                                                                  //<- #3
        x = x--;
        y = y++;
    }
    ViewBag.MonthList = items;
    return View();
}

我在“item”变量上遇到了几个构建错误,我不知道如何修复它们:

1 - 错误的数组声明符:要声明托管数组,等级说明符位于变量标识符之前。要声明固定大小的缓冲区字段,请在字段类型之前使用 fixed 关键字。变量声明中不能指定数组大小(尝试用 'new' 表达式初始化)

2 – 在此范围内已经定义了一个名为“item”的局部变量(我知道我需要摆脱重复,但这只是我整体逻辑问题的一个症状)。

3 – 无法将带有 [] 的索引应用于 SelectListItem

类型的表达式

显然,c# 不喜欢我尝试在我的列表项上使用索引,如 "item[y]"。我以为我很聪明,但是这种方法完全是炸弹。

我搜索了这个站点和 Internet,并阅读了我找到的所有示例,但找不到符合我需要的解决方案。任何人都可以就如何修复此逻辑提供一些指导吗?

我相信我已经彻底解释了这个问题,但如果我不够准确或不够清晰,请提前接受我的道歉。我很乐意回应有关更多信息的请求。

                      *******QUESTION EDIT***********

这是一个粘性的(无论如何对我来说)。然而,Vlad Stryapko 让我走上了正确的轨道,几个星期后,我得到了他的解决方案(我接受了)。还有一个问题,但与我原来的 post 无关,所以我将 post 单独提出一个问题。这是我的最终代码:

我的视图模型

        public SelectList MonthList { get; set; }


        public SelectList ReverseMonthsLists()
        {
            var stringViewOfDates = GetDates().Select(_ => _.ToString("MMM yyyy")).ToList();

            var list = new List<SelectListItem>();
            list.Add(new SelectListItem { Selected = true, Text = stringViewOfDates[0], Value = stringViewOfDates[0] });

            for (int i = 1; i < stringViewOfDates.Count(); i++)
            {
                list.Add(new SelectListItem { Selected = false, Text = stringViewOfDates[i], Value = stringViewOfDates[i] });
            }
            var selectList = new SelectList(list);
            return selectList;
        }


        public static IEnumerable<DateTime> GetDates()
        {
            DateTime startDate = new DateTime(2017, 6, 1).Date;
            var currentDate = DateTime.Now.Date;

            int numberOfMonthsToShow = (currentDate.Year - startDate.Year) * 12 + currentDate.Month - startDate.Month;
            if (numberOfMonthsToShow == 0)
                numberOfMonthsToShow = 1;

            var dates = new List<DateTime>(numberOfMonthsToShow);
            for (int i = 0; i < numberOfMonthsToShow; i++)
            {
                dates.Add(currentDate);
                currentDate = currentDate.AddMonths(-1);
            }
            return dates;
        }

我的Html查看

        @Html.DropDownList("selectList", Model.ReverseMonthsLists(), "Month Selector")

代码现在可以编译,但我得到的只是下拉列表中的 "System.Web.Mvc.SelectListItem"。我会在另一个线程中提出这个问题,因为我不相信我可以在这里问第二个问题。

我还不太了解您的需要,下面是一个示例,说明如何构建从当前日期开始并包含 12 个月的日期列表。

public static IEnumerable<DateTime> GetDates()
{
    var currentDate = DateTime.Now;
    var numberOfMonthsToShow = 12;
    var dates = new List<DateTime>(numberOfMonthsToShow);
    for (int i = 0; i < numberOfMonthsToShow; i++)
    {
        dates.Add(currentDate);
        currentDate = currentDate.AddMonths(-1);
    }
    return dates;
}

如果要从上个月开始,将currentDate初始化为DateTime.Now.AddMonths(-1)

接下来,您必须构建这些日期的字符串表示形式。对于您的格式,请使用 Y 格式选择器。

 var stringViewOfDates = GetDates().Select(_ => _.ToString("MMM yyyy"));

现在您已经有了字符串,可以将它们添加到 SelectList 中。我现在没有 ASP.NET MVC,但它应该很简单。类似于:

var list = new List<SelectListItem>();
list.Add(new SelectListItem { Selected = true, Text = stringViewOfDates[0], Value = stringViewOfDates[0]})

for (int i = 1; i < stringView.Count(); i++)
{
  list.Add(new SelectListItem { Selected = false, Text = stringViewOfDates[i], Value = stringViewOfDates[i]);
}

var selectList = new SelectList(list);

您有很多代码对于 'give me previous months' 的任务来说似乎是多余的。请尝试我提供的代码或解释为什么您需要如此复杂的代码。我可能漏掉了你的一些逻辑。

给定一个通用函数来获取日期列表:-

public static IEnumerable<string> ReverseMonthList(string format, DateTime start, int count)
    {
        return Enumerable.Range(0, count).Select(n => start.AddMonths(-(n + 1)).ToString(format));
    }

然后

var from = new DateTime(2016, 7, 5);
var now = new DateTime(2017, 7, 5);
var monthsSinceStart = ((now.Year - @from.Year) * 12) + now.Month - @from.Month;
var dates = ReverseMonthList("MMM yyyy", now, monthsSinceStart);

给予

The list is unusual because it must populate backward from last month up until a year ago.

这很容易做到。首先 - 生成日期。您有开始日期(上个月)和所需的总月数 (12)。然后将每个日期投影到 SelectListItem:

var today = DateTime.Today;
var startOfMonth = new DateTime(today.Year, today.Month, 1);

var items = from i in Enumerable.Range(1, 12)
            let date = startOfMonth.AddMonths(-i)
            select new SelectListItem { 
                Text = date.ToString("MMM yyyy"), 
                Value = date.ToString("MMM"), 
                Selected = i == 1
            };

ViewBag.MonthList = items.ToList();

备注:

  1. 您应该计算相对于当月开始的日期。否则,2 月 29 日可能会有问题。
  2. 使用字符串格式以您想要的格式生成日期。不要格式化日期的部分并将它们连接起来。
  3. 年份格式字符串为小写 yyyy
  4. SelectListItem item[y] = ... 是不正确的语法。它是赋值和声明的混合体。如果你想在列表中分配一个项目,那么你应该通过索引 item[y] = ... 来引用该项目。但是您应该先检查列表是否包含足够的项目。实际上你真正需要做的是在列表中添加一个项目,这是通过 items.Add(...).
  5. 实现的
  6. 考虑学习 LINQ - 它允许您编写查询(以声明方式)以轻松过滤、投影等数据

这是一个控制台应用程序

   var startDate = DateTime.Now.Date;

    var days = new List<Tuple<string, string>>();

    int totalMonths = 12;

    for (int i = 1; i < totalMonths; i++)
    {
        var tempDay = startDate.AddMonths(-i);
        days.Add(new Tuple<string, string>($"{tempDay.ToString("MMM")} {tempDay.ToString("yyyy")}", tempDay.ToString("MMM")));
    }

    foreach (var d in days)
    {
        Console.WriteLine(d.Item1);
    }

输出 2017 年 6 月 2017 年 5 月 2017 年 4 月 2017 年 3 月 2017 年 2 月 2017 年 1 月 2016 年 12 月 2016 年 11 月 2016 年 10 月 2016 年 9 月 2016 年 8 月

出于测试目的,我使用了 tuple。使用列表项 class。对于项目选择,使用单独的循环或在生成方法中使用 if 条件。

使用 LINQ 并倒退:

var ans = Enumerable.Range(1, 12).Select(p => start.AddMonths(-p).ToString("MMM yy")).ToList();