2009-10-29 67 views
3

在MVC2.0中使用模板助手我遇到了dillema,如何讓項目填充下拉列表。 我正在使用[UIHint(BadgesDropDown)]屬性,但是如何在不違反MVC模式的情況下獲取列表項,控制器是否應將它們放置在ViewData中? BadgesDropDown.ascx是否應該調用Helper來獲取它們?MVC模板助手 - DropDown

現在我打算:

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"); 

這是要走的路?

回答

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]:searcch爲codeclimber.net.nz以及如何對創建-A-下拉列表與 - asp.net- mvc [2]:bradwilson.typepad.com和templates-part-5-master-page-templates

-1

我像上面的例子一樣實施瞭解決方案。要注意的一件事是,助手應該只提供給他們的數據工作,看View dependency

最好的做法是寫HTML 助手不知道控制器和 上下文。他們應該根據調用者提供的數據 來完成 的工作。

我同意上述說法。只是與常規的ASP.Net開發相比,需要做很多工作。

2

最近有很多關於這個話題的討論。在日期,日期範圍和多選複選框列表中遇到類似的障礙。任何地方你可能想使用豐富的html控件。我一直在試驗兒童ViewModels的概念,我認爲解決方案比我嘗試過的其他方法更清潔。

基本的概念是,您可以定義一個與自定義EditorTemplate緊密耦合的小視圖模型。

在你的榜樣,我們會用一個(孩子)視圖模型這是具體到一個單一的選擇列表開始:

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。

然後在共享/ 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是一個自定義的擴展,它確實明顯

訣竅這裏是ID和名稱使用設置默認ModelBinder期望的複合格式。在我們的例子「Priority.Value」中。因此,被定義爲SelectModel的一部分的基於字符串的Value屬性被直接設置。如果我們需要重新顯示錶單,setter負責更新Items的列表以設置默認選擇選項。

這個「子視圖模型」方法真正發揮的是更復雜的「控件標記片段」。我現在有多個子視圖模型,它們遵循MultiSelect列表,開始/結束日期範圍和日期+時間組合的類似方法。

只要你走下這條路,下一個明顯的問題就變成了驗證。

我結束了有所有我的孩子的視圖模型的實現標準接口:

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; }