65.9K
CodeProject 正在变化。 阅读更多。
Home

一个 ASP.Net MVC 中适用于所有下拉列表的 EditorTemplate

starIconstarIconstarIconstarIconstarIcon

5.00/5 (5投票s)

2012年1月9日

CPOL

3分钟阅读

viewsIcon

57997

downloadIcon

1277

在 OnResultExecuting 中获取 DropDownList 项。

引言

每个 ASP.Net MVC 程序员都知道当将 ViewModel 发送到相应的 View 时,需要简化 DropDownList。

这就是我如何做的,并且还有许多其他例子可以自动化这个无聊的任务。

背景

几乎每个编辑 ViewModel 至少需要一个 DropDownList 来处理 Model 或数据库中的 ForeignKey。

几乎所有 ASP MVC 程序员都使用 Html.DropDownList(...) 或 Html.DropDownListFor(...) 帮助器方法。 为了填充这个 DropDownList,他们从 Controller 中的 Data Store 中提取项目,并将它们放入 ViewData 或 ViewBag,然后在帮助器方法中使用这些值。

但现在我将使用一个 EditorTemplate 来处理应用程序中的所有 DropDownList。

代码示例

这就是我试图做的

    public class PersonEditor
    {
        public int Id { get; set; }
        public string Name { get; set; }

        [DropDown("GetServices", 1)]
        public int? ServiceId { get; set; }
    } 

然后我需要我的 Controller 在 View 执行之前为我处理这个导航属性,并传递我添加的任何参数,例如本例中的数字 1。

类似这样

    [FillDropDowns]
    public ActionResult Edit()
    {
        var viewModel = new PersonEditor() {ServiceId = 3};
        return View(viewModel);
    } 

很明显,我们需要一个 Attribute 来装饰导航属性,我们还需要一个 ActionFilter,它将在 View 呈现之前执行以填充 ViewData 字典中的项目。

假设这是我们的简单的 DropDownListServiceLayer

public class DropDownListService
{
..........
    public IEnumerable<SelectListItem> GetServices (int typeId)
    {
        return context.Services
            .Where(m => m.Type == typeId)
            .Select(m => new SelectListItem {Text = m.Name, Value = m.Id.ToString()});
    }
} 

然后我们需要在 View 呈现之前调用 GetServices 方法,并将结果存储在某个 ViewData 键中,以便在 View 中检索它。

让我们继续吧 :).
DropDownAttribute

代码

[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
public class DropDownAttribute : UIHintAttribute
{
    private readonly Type _serviceType;
    private readonly string _methodName;
    private readonly object[] _arguments;


        public DropDownAttribute(string methodName, params object[] arguments)
            : this(methodName, "DropDown", typeof(DropDownListService), arguments)
        {
        }

        public DropDownAttribute(string methodName, string templateName, Type serviceType, params object[] arguments)
            : base(templateName)
        {
            _serviceType = serviceType;
            _comboBoxServiceMethods = methodName;
            _arguments = arguments;
        }


    public IEnumerable<SelectListItem> GetMethodResult()
    {
        if (_serviceType == null)
            throw new NoNullAllowedException("Service class type is needed.");
        try
        {

            var serviceInstance = Activator.CreateInstance(_serviceType);
            var methodInfo = _serviceType.GetMethod(_methodName);
            return methodInfo.Invoke(serviceInstance, _arguments) as IEnumerable<SelectListItem>;
        }
        catch (Exception)
        {
            throw;
        }
    }
} 

很明显,这个 Attribute 继承自 "UIHintAttribute" 以获取 Editor 模板的名称。 我们需要 Service 类型,它将为我们提供 IEnumerable<SelectListItem>,并且有一个用于默认 Service 类型的重载构造函数。

然后我们需要方法名称和它将接受的参数,因此我添加了 params 关键字以将所需参数传递给此方法。

GetMethodResult 方法将从 ActionFilter 中调用以执行它并获取结果。

ActionFilter

代码

public class FillDropDowns : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        var viewModel = filterContext.Controller.ViewData.Model;
        if (viewModel != null)
            setLists(viewModel.GetType(), filterContext.Controller.ViewData);

        base.OnResultExecuting(filterContext);
    }

    private static void setLists(Type viewModelType, IDictionary<string, object> viewData)
    {
        foreach (var property in viewModelType.GetProperties())
        {
            if (!(property.PropertyType.IsClass && !(property.PropertyType == typeof(string))))
            {
                var att = (DropDownAttribute)GetCustomAttribute(property, typeof(DropDownAttribute));
                if (att != null)
                {
                    var viewDataKey = "DDKey_" + property.Name;
                    viewData[viewDataKey] = viewData[viewDataKey] ?? att.GetMethodResult();
                }
            }
            else
            {
                setLists(property.PropertyType, viewData);
            }
        }
    }
} 

ActionFilter 将在 ViewModel 中查找任何使用 DropDownAttribute 装饰的属性,如果 ViewData 字典中尚没有这些列表,那么我们将调用 GetMethodResult 并在 ViewData 中使用相应的 Key 存储此列表。

您可能注意到,我添加了 "DDKey_" 作为此 key 的前缀,这是因为 ASP.Net MVC 3 在 ViewData 中设置与您的属性名称相同的名称时存在问题。

另一件事是,如果您有嵌套的 ViewModels,那么递归将循环遍历它们并填充其导航属性。

Editor 模板

在 EditorTemplates 文件夹中添加新视图,作为所有下拉导航属性的编辑器。 并且当我们使用 UIHint Attribute 装饰我们的属性时,EditorTemplate 文件应该命名为 "DropDown",这就是它的简单代码

 
@model object
@Html.DropDownListFor(m => m, Html.GetAutomatedList(m => m).SetSelected(Model))

阅读下一部分,了解 "GetAutomatedList" 和 "SetSelected" 帮助器方法,

Html 扩展帮助器

最后,我们只需要两个帮助器方法来完成工作

public static class HtmlExtensions
{
    public static IEnumerable<SelectListItem> SetSelected(this IEnumerable<SelectListItem> selectList, object selectedValue)
    {
        selectList = selectList ?? new List<SelectListItem>();
        if (selectedValue == null)
            return selectList;
        var vlaue = selectedValue.ToString();
        return selectList
            .Select(m => new SelectListItem
                                {
                                    Selected = string.Equals(vlaue, m.Value),
                                    Text = m.Text,
                                    Value = m.Value
                                });
    }


    public static IEnumerable<SelectListItem> GetAutomatedList<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
                                                                                    Expression<Func<TModel, TProperty>> expression)
    {
        var metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        return ((IEnumerable<SelectListItem>) htmlHelper.ViewData["DDKey_" + metadata.PropertyName]);
    }
} 
GetAutomatedList 扩展方法将通过 ViewModel 属性名称从 ViewData 获取所需的列表。

而另一个方法 SetSelected 只是将 Selected 属性设置为 SelectListItem。



最后

这是我的第一篇文章,正如您刚刚发现的那样,我的语言不是很好,但我希望这个想法是清晰且有帮助的。
感谢您的时间。

© . All rights reserved.