【问题标题】:Best way to sort a DropDownList in MVC3 / Razor using helper method使用辅助方法在 MVC3/Razor 中对 DropDownList 进行排序的最佳方法
【发布时间】:2011-08-28 19:02:38
【问题描述】:

您好,我对 MVC3 和 Razor 还是很陌生,过去几天我一直在努力解决这个问题。我的项目架构师给了我一个任务,即创建一个辅助方法,用于对 MVC 视图中的下拉列表进行排序。我有一个从控制器检索各种数据的视图,并且我正在返回一些我想出现在下拉列表中的值。我被告知不要在 Controller 中对其进行排序,并将我们想要排序的字段传递给辅助方法。我可以像下面那样做,但架构师希望保持视图不受 c 尖锐代码的影响:

@Html.DropDownListFor(model => model.StudyName, new SelectList(ViewBag.StudyTypes, "Value", "Text").OrderBy(l => l.Text))

所以我创建了一些示例代码和一些扩展方法来尝试让它工作。我的想法是复制现有的 Html.DropDownList 方法并允许传递“object htmlAttributes”,这样我就可以将样式设置为方法调用的一部分。

到目前为止,这是我的代码。我在 Edit Controller 方法中返回 ViewBag.StudyTypes 中的下拉数据:

public ActionResult Edit(int id)
{
    IEnumerable<SelectListItem> mySelectList = new List<SelectListItem>();
    IList<SelectListItem> myList = new List<SelectListItem>();

    for (int i = 0; i < 5; i++)
    {
        myList.Add(new SelectListItem() 
            { Value = i.ToString(), Text = "My Item " + i.ToString(), Selected = i == 2 }
        );
    }

    mySelectList = myList;
    ViewBag.StudyTypes = mySelectList;

    StudyDefinition studydefinition = db.StudyDefinitions.Find(id);

    return View(studydefinition);
}

这是我的查看代码:

    @model MyStudyWeb.Models.StudyDefinition

@using MyStudyWeb.Helpers

@{
    ViewBag.Mode = "Edit";
}
<div>
    @Html.DropDownListSorted(new SelectList(ViewBag.StudyTypes, "Value", "Text"))<br />
    @Html.DropDownListSorted("MyList", new SelectList(ViewBag.StudyTypes, "Value", "Text"))<br />
</div>

最后,下面是我尝试使用的扩展方法。第一个扩展方法什么都不做,我只是在视图中得到一个空白区域。第二种方法有效,但很丑陋。对于第三种方法,我不知道如何指定“order by”参数,因为 IEnumerable 上的 OrderBy 需要 Linq 表达式。

namespace StudyDefinition.Helpers
{
    public static class HtmlHelperExtensions
    {
        // 1st sort method: sort the passed in list and return a new sorted list
        public static SelectList DropDownListSorted(this HtmlHelper helper, IEnumerable<SelectListItem> selectList)
        {
            var x = new SelectList(selectList.ToList()).OrderBy(l => l.Text);

            return x as SelectList;
        }

        // 2nd sort method: return IHtml string and create <select> list manually
        public static IHtmlString DropDownListSorted(this HtmlHelper helper, string name, SelectList selectList)
        {
            StringBuilder output = new StringBuilder();
            (selectList).OrderBy(l => l.Text);

            output.Append("<select id=" + name + " name=" + name + ">");

            foreach (var item in selectList)
            {
                output.Append("<option value=" + item.Value.ToString() + ">" + item.Text + "</option>");
            }

            output.Append("</select>");

            return MvcHtmlString.Create(output.ToString());
        }

        // 3rd sort method: pass in order by parameter - how do I use this?
        public static IHtmlString DropDownListSorted(this HtmlHelper helper, string name, SelectList selectList, string orderBy)
        {
            StringBuilder output = new StringBuilder();

            //How do I use the orderBy parameter?
            (selectList).OrderBy(l => l.Text);

            output.Append("<select id=" + name + " name=" + name + ">");

            foreach (var item in selectList)
            {
                output.Append("<option value=" + item.Value.ToString() + ">" + item.Text + "</option>");
            }

            output.Append("</select>");

            return MvcHtmlString.Create(output.ToString());
        }        
    }
}

我真的不知道最好的方法,可能有一个更简单的方法,我完全想念了,我可能已经看不到树木了。一些问题

  • 我应该返回 SelectList 还是 MvcHtmlString,还是完全返回其他内容?

  • 对于第一个扩展方法,如何让返回的 SelectList 在 View 中呈现?

  • 如何将参数传递给指定排序顺序的扩展方法?

  • 如何传递“object htmlAttributes”参数,以及如何将此对象/参数应用到 SelectList?

如果有人有一些想法或建议,我将不胜感激:)

【问题讨论】:

    标签: asp.net-mvc-3 razor drop-down-menu


    【解决方案1】:

    代码的第一部分也是最重要的部分是去掉任何ViewBag/ViewData(我个人认为这对MVC 应用程序来说是癌症)并使用视图模型和强类型视图。

    让我们从定义一个视图模型开始,该模型将代表我们的视图将使用的数据(本例中为下拉列表):

    public class MyViewModel
    {
        public string SelectedItem { get; set; }
        public IEnumerable<SelectListItem> Items { get; set; }
    }
    

    那么我们可以有一个控制器:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var model = new MyViewModel
            {
                // I am explicitly putting some items out of order
                Items = new[]
                {
                    new SelectListItem { Value = "5", Text = "Item 5" },
                    new SelectListItem { Value = "1", Text = "Item 1" },
                    new SelectListItem { Value = "3", Text = "Item 3" },
                    new SelectListItem { Value = "4", Text = "Item 4" },
                }
            };
            return View(model);
        }
    }
    

    还有一个观点:

    @model MyViewModel
    @Html.DropDownListForSorted(
        x => x.SelectedItem, 
        Model.Items, 
        new { @class = "foo" }
    )
    

    最后一部分是帮助方法,它将按值对下拉列表进行排序(您可以将其调整为按文本排序):

    public static class HtmlExtensions
    {
        public static IHtmlString DropDownListForSorted<TModel, TProperty>(
            this HtmlHelper<TModel> helper, 
            Expression<Func<TModel, TProperty>> expression, 
            IEnumerable<SelectListItem> items, 
            object htmlAttributes
        )
        {
            var model = helper.ViewData.Model;
            var orderedItems = items.OrderBy(x => x.Value);
            return helper.DropDownListFor(
                expression, 
                new SelectList(orderedItems, "Value", "Text"), 
                htmlAttributes
            );
        }
    }
    

    【讨论】:

    • 嗨@Darin,非常感谢。不过有几个问题,我正在处理现有系统,所有视图都有多个下拉列表,它们都作为 ViewBag 属性从它们的控制器返回。所以我不能真正改变这种方法。其次,视图已经在做实体类的“返回视图(studydefinition)”。还有另一种方法可以返回视图模型吗?第三,当我添加您的扩展方法时,无法识别 DropDownListFor 方法,我得到:“System.Web.Mvc.HtmlHelper”不包含“DropDownListFor”的定义
    • @Ciaran Bruen,适用于现有系统和 ViewBag。我只是在以后的代码中提到它,以避免这种不良做法。如果您有多个下拉列表,您只需在视图模型上有多个 IEnumerable&lt;SelectListItem&gt; 属性。关于return View(studydefinition),这是将视图模型传递给视图的正确方法。第三,DropDownListFor 扩展方法是在 System.Web.Mvc.Html 命名空间中定义的,因此只需在包含 DropDownListForSorted 帮助器的文件顶部的该命名空间中添加一个 using 即可将其纳入范围。
    【解决方案2】:

    只需在将项目返回下拉列表之前添加排序即可。

    这样做:

    模型:StudyViewModel.cs

    public class StudyViewModel {
        public string StudyName { get; set; }
        public string StudyTypes { get; set; }
    }
    

    控制器:StudyController.cs

    using System.Web.Mvc;
    public class StudyController 
    {
        public List<SelectListItem> studyTypes() 
        {
            List<SelectListItem> itemList = new List<SelectListItem>();
            for (var i=0; i<5; i++)
            {
                itemList.Add = new SelectListItem({
                    Value = i.ToString();
                    Text = "My Item";
                });
            }
            // You can sort here....
            List<SelectListItem> sortedList = itemList.OrderBy(x=>x.Text);
            return sortedList;
        }
    
        public ActionResult Edit(int id)
        {
            //You won't need this because you get it using your
            //controller's routine, instead 
            //ViewBag.StudyTypes = studySlots.OrderBy(e => e.Value);
    
            //-- unless you need to add these values to the model for 
            // some reason (outside of filling the ddl), in which case....
            // StudyViewModel svm = new StudyViewModel();
            // svm.StudyTypes = studySlots.OrderBy(e => e.Value);
            // svm.StudyName = "My Item";
            // return View(svm);
    
            // Otherwise, just....
            return View();
        }
    }
    

    查看:Edit.cshtml

    @Html.DropDownListFor(model => model.StudyName)
        .OptionLabel('Select...')
        .DataTextField('Text')
        .DataValueField('Value')
        .Datasource(source => 
        {
           // This is where you populate your data from the controller
            source.Read(read => 
            {
                read.Action("studyTypes", "Study");
            });
        })
        .Value(Model.StudyName != null ? Model.StudyName.ToString() : "")
    )
    

    这种方式将避免 ViewBags,直接使用函数填充值。

    【讨论】:

      【解决方案3】:

      如果您使用的是数据库,则可以使用查询来定义排序元素

      using (BDMMContext dataContext = new BDMMContext())
      {
      foreach (Arquiteto arq in dataContext.Arquitetos.SqlQuery("SELECT * FROM  Arquitetos ORDER BY Nome"))
                          {
                              SelectListItem selectItem = new SelectListItem { Text = arq.Nome, Value = arq.Arquiteto_Id.ToString() };
                              //
                              list.Add(selectItem);
                          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-07
        • 2012-11-20
        • 2016-06-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多