3

在 MVC2.0 中使用模板化助手时,我遇到了一个难题,如何让项目填充下拉列表。我正在使用一个[UIHint(BadgesDropDown)]属性,但是我将如何在不违反 MVC 模式的情况下获取列表项,控制器应该将它们放在 ViewData 中吗?是否应该BadgesDropDown.ascx调用 Helper 来获取它们?

现在我要去:

BadgesDropDown.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%= Html.DropDownList("", ViewData["Badges"] as IEnumerable<SelectListItem>)%>

控制器

ViewData["Badges"] = new SelectList(SiteRepository.GetBadges(), "RowKey", "BadgeName");

这是要走的路吗?

4

3 回答 3

2

最近有很多关于这个话题的讨论。日期、日期范围和多选复选框列表也会遇到类似的障碍。任何您可能想要使用一组丰富的 html 控件的地方。我一直在尝试子 ViewModel 的概念,我认为该解决方案比我尝试过的其他方法更干净。

基本概念是定义一个与自定义 EditorTemplate 紧密耦合的小型视图模型。

在您的示例中,我们将从特定于单个选择列表的(子)ViewModel 开始:

public class SelectModel
{
  #region SelectModel(string value, IEnumerable<SelectListItem> items)
  public SelectModel(string value, IEnumerable<SelectListItem> items)
  {
    _value = value;
    Items = new List<SelectListItem>(items);

    _Select();
  } 
  #endregion

  // Properties

  public List<SelectListItem> Items { get; private set; }

  public string Value
  { 
    get { return _value; }
    set { _value = value; _Select();}
  }
  private string _value;

  // Methods

  private void _Select()
  {
    Items.ForEach(x => x.Selected = (Value != null && x.Value == Value));
  }
}

在想要使用下拉列表的视图模型中,您可以编写选择模型(我们都在使用视图模型,对吧?):

public class EmailModel
{
  // Constructors

  public EmailModel()
  {
    Priority = new SelectModel("normal", _ToPrioritySelectItems());
  }

  // Properties

  public SelectModel Priority { get; set; }

  // Methods

  private IEnumerable<SelectListItem> _ToPrioritySelectItems()
  {
    List<SelectListItem> result = new List<SelectListItem>();

    result.Add(new SelectListItem() { Text = "High", Value = "high" });
    ...
  }

请注意,这是一个带有一组固定下拉项的简单示例。如果它们来自域层,控制器会将它们传递给 ViewModel。

然后在 Shared/EditorTemplates 中添加一个编辑器模板 SelectModel.ascx

<%@ Control Inherits="System.Web.Mvc.ViewUserControl<SelectModel>" %>

<div class="set">
  <%= Html.LabelFor(model => model) %>
  <select id="<%= ViewData.ModelMetadata.PropertyName %>_Value" name="<%=ViewData.ModelMetadata.PropertyName %>.Value">
  <% foreach (var item in Model.Items) { %>
    <%= Html.OptionFor(item) %>
  <% } %>
  </select>
</div>

注意:OptionFor 是一个自定义扩展,它做的很明显

这里的技巧是使用默认 ModelBinder 期望的复合格式设置 id 和 name。在我们的示例“Priority.Value”中。因此,直接设置定义为 SelectModel 一部分的基于字符串的 Value 属性。如果我们需要重新显示表单,setter 负责更新项目列表以设置默认选择选项。

这种“子视图模型”方法真正闪耀的地方是更复杂的“标记控制片段”。我现在有子视图模型,它们对多选列表、开始/结束日期范围和日期+时间组合遵循类似的方法。

一旦你沿着这条路走下去,下一个明显的问题就是验证。

我最终让我所有的孩子 ViewModel 实现了一个标准接口:

public interface IValidatable
{
  bool HasValue { get; }
  bool IsValid { get; }
}

然后,我有一个自定义的 ValidationAttribute:

public class IsValidAttribute : ValidationAttribute
{
  // Constructors

  public IsValidAttribute()
  {
    ErrorMessage = "(not valid)";
  }

  // Properties

  public bool IsRequired { get; set; }

  // Methods

  private bool Is(object value)
  {
    return value != null && !"".Equals(value);
  }

  public override bool IsValid(object value)
  {
    if (!Is(value) && !IsRequired)
      return true;

    if (!(value is IValidatable))
      throw new InvalidOperationException("IsValidAttribute requires underlying property to implement IValidatable");

    IValidatable validatable = value as IValidatable;
    return validatable.IsValid;
  }
}

现在,您可以像任何标量属性一样将属性放在基于子 ViewModel 的属性上:

[IsValid(ErrorMessage = "Please enter a valid start date/time")]
public DateAndTimeModel Start { get; set; }
于 2010-06-29T04:40:09.390 回答
0

在 MVC 2 中,一个很棒的新方法......如果使用它依赖于所有属性数据。

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<glossaryDB.EntityClasses.AssociationEntity>" %>

<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server">
    Association: Edit
</asp:Content>

<asp:Content ID="Content3" ContentPlaceHolderID="MainContent" runat="server">
    <h3>Association: Edit</h3>
    <% using (Html.BeginForm()) { %>
        <fieldset style="padding: 1em; margin: 0; border: solid 1px #999;">
            <%= Html.ValidationSummary("Edit was unsuccessful. Please correct the errors and try again.") %>
            <%= Html.EditorForModel() %>
            <input type="submit" value="  Submit  " />
        </fieldset>
    <% } %>
    <p><%= Html.ActionLink("Details", "Index") %></p>
</asp:Content>

为此,有 2 个选项。UIHint 必须提供数据源或控制器必须提供。如果 UIHint 确实如此,则提供给下拉列表的数据是固定的。另一个选项是控制器,它允许我们根据需要使用不同的数据集切换下拉数据。

我发现了一些相关的例子:

书呆子晚餐

[1]:搜索 codeclimber.net.nz 和 how-to-create-a-dropdownlist-with-asp.net-mvc [2]:bradwilson.typepad.com 和 templates-part-5-master-page-templates

于 2009-11-09T00:36:42.573 回答
-1

我按照上面的例子实现了解决方案。需要注意的一件事是助手应该只使用提供给他们的数据,请参阅查看依赖项

最佳实践是编写不知道控制器和上下文的 Html 助手。他们应该只根据调用者提供的数据来完成他们的工作。

我同意上述说法。只是与常规的 ASP.Net 开发相比,需要做很多工作。

于 2009-11-06T12:16:01.353 回答