diff --git a/samples/MvcSample.Web/HomeController.cs b/samples/MvcSample.Web/HomeController.cs index cffef930c0..46402ec9b3 100644 --- a/samples/MvcSample.Web/HomeController.cs +++ b/samples/MvcSample.Web/HomeController.cs @@ -57,7 +57,7 @@ namespace MvcSample.Web public ActionResult Edit(User user) { ViewBag.Address = _addresses; - ViewBag.Age = _ages; + ViewBag.Ages = _ages; ViewBag.Gift = "the banana"; return View("Create"); diff --git a/samples/MvcSample.Web/Models/User.cs b/samples/MvcSample.Web/Models/User.cs index 74e8bf54a3..c1496495da 100644 --- a/samples/MvcSample.Web/Models/User.cs +++ b/samples/MvcSample.Web/Models/User.cs @@ -15,12 +15,19 @@ // See the Apache 2 License for the specific language governing // permissions and limitations under the License. +using System.Collections.Generic; using System.ComponentModel.DataAnnotations; namespace MvcSample.Web.Models { public class User { + public User() + { + OwnedAddresses = new List(); + ParentsAges = new List(); + } + [Required] [MinLength(4)] public string Name { get; set; } @@ -35,5 +42,9 @@ namespace MvcSample.Web.Models public string Profession { get; set; } public string About { get; set; } public string Log { get; set; } + public IEnumerable OwnedAddresses { get; private set; } + + // This does not bind correctly. Only gets highest value. + public List ParentsAges { get; private set; } } } \ No newline at end of file diff --git a/samples/MvcSample.Web/MvcSample.Web.kproj b/samples/MvcSample.Web/MvcSample.Web.kproj index ddbfaf1c7f..5facbdc4cd 100644 --- a/samples/MvcSample.Web/MvcSample.Web.kproj +++ b/samples/MvcSample.Web/MvcSample.Web.kproj @@ -24,6 +24,7 @@ + diff --git a/samples/MvcSample.Web/Views/Home/Create.cshtml b/samples/MvcSample.Web/Views/Home/Create.cshtml index b93085eba6..4f94683fcf 100644 --- a/samples/MvcSample.Web/Views/Home/Create.cshtml +++ b/samples/MvcSample.Web/Views/Home/Create.cshtml @@ -2,7 +2,7 @@ @using Microsoft.AspNet.Mvc.ModelBinding @model User @{ - ViewBag.Title = (Model == null) ? "Create Page" : "Edit Page"; + ViewBag.Title = (Model == null && ViewData.ModelState.Count == 0) ? "Create Page" : "Edit Page"; if (ViewBag.Gift == null) { ViewBag.Gift = "nothing"; @@ -45,7 +45,7 @@ + + + + + + + +
- + @Html.TextBox("Name") @@ -56,7 +56,7 @@
- + @Html.DropDownList("Address", "Select an Address") @@ -64,7 +64,7 @@
- + @Html.DropDownListFor(model => model.Age, (IEnumerable)ViewBag.Ages, htmlAttributes: new { @class = "form-control" }) @@ -89,6 +89,22 @@ @Html.TextArea("About", "You can explain about your hobbies, work etc.", 5, 40, htmlAttributes: new { style = "font-weight:bold" })
+ + + @Html.Editor("OwnedAddresses") +
+ + + @Html.ListBoxFor(model => model.ParentsAges, (IEnumerable)ViewBag.Ages, htmlAttributes: new { @class = "form-control" }) +
diff --git a/samples/MvcSample.Web/Views/Home/EditorTemplates/IEnumerable`1.cshtml b/samples/MvcSample.Web/Views/Home/EditorTemplates/IEnumerable`1.cshtml new file mode 100644 index 0000000000..8982023b3f --- /dev/null +++ b/samples/MvcSample.Web/Views/Home/EditorTemplates/IEnumerable`1.cshtml @@ -0,0 +1,4 @@ +@using System.Collections.Generic +@model IEnumerable + +@Html.ListBox(null, (IEnumerable)ViewBag.Address) \ No newline at end of file diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs index 5cc92eb796..c2e99616d0 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelper.cs @@ -332,6 +332,12 @@ namespace Microsoft.AspNet.Mvc.Rendering htmlAttributes); } + /// + public HtmlString ListBox(string name, IEnumerable selectList, object htmlAttributes) + { + return GenerateListBox(metadata: null, name: name, selectList: selectList, htmlAttributes: htmlAttributes); + } + /// public virtual HtmlString Name(string name) { @@ -866,6 +872,21 @@ namespace Microsoft.AspNet.Mvc.Rendering return tagBuilder.ToHtmlString(TagRenderMode.Normal); } + protected HtmlString GenerateListBox( + ModelMetadata metadata, + string name, + IEnumerable selectList, + object htmlAttributes) + { + return GenerateSelect( + metadata, + optionLabel: null, + name: name, + selectList: selectList, + allowMultiple: true, + htmlAttributes: htmlAttributes); + } + protected virtual HtmlString GeneratePassword(ModelMetadata metadata, string name, object value, object htmlAttributes) { @@ -974,6 +995,14 @@ namespace Microsoft.AspNet.Mvc.Rendering // If we got a null selectList, try to use ViewData to get the list of items. if (selectList == null) { + if (string.IsNullOrEmpty(name)) + { + // Avoid ViewData.Eval() throwing an ArgumentException with a different parameter name. Note this + // is an extreme case since users must pass a non-null selectList to use CheckBox() or ListBox() + // in a template, where a null or empty name has meaning. + throw new ArgumentException(Resources.ArgumentCannotBeNullOrEmpty, "name"); + } + selectList = GetSelectListItems(name); usedViewData = true; } diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs index e971e75ae0..5163c07160 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/Html/HtmlHelperOfT.cs @@ -170,6 +170,18 @@ namespace Microsoft.AspNet.Mvc.Rendering return GenerateLabel(metadata, ExpressionHelper.GetExpressionText(expression), labelText, htmlAttributes); } + /// + public HtmlString ListBoxFor( + [NotNull] Expression> expression, + IEnumerable selectList, + object htmlAttributes) + { + var metadata = GetModelMetadata(expression); + var name = ExpressionHelper.GetExpressionText(expression); + + return GenerateListBox(metadata, name, selectList, htmlAttributes); + } + /// public HtmlString NameFor([NotNull] Expression> expression) { diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperSelectExtensions.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperSelectExtensions.cs index cd79f2ba33..0b3f06eaed 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperSelectExtensions.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/HtmlHelperSelectExtensions.cs @@ -79,5 +79,26 @@ namespace Microsoft.AspNet.Mvc.Rendering { return htmlHelper.DropDownListFor(expression, selectList, optionLabel, htmlAttributes: null); } + + public static HtmlString ListBox([NotNull] this IHtmlHelper htmlHelper, string name) + { + return htmlHelper.ListBox(name, selectList: null, htmlAttributes: null); + } + + public static HtmlString ListBox( + [NotNull] this IHtmlHelper htmlHelper, + string name, + IEnumerable selectList) + { + return htmlHelper.ListBox(name, selectList, htmlAttributes: null); + } + + public static HtmlString ListBoxFor( + [NotNull] this IHtmlHelper htmlHelper, + [NotNull] Expression> expression, + IEnumerable selectList) + { + return htmlHelper.ListBoxFor(expression, selectList, htmlAttributes: null); + } } } diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs index 1b2c2fe263..4668d56bbc 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelper.cs @@ -294,6 +294,19 @@ namespace Microsoft.AspNet.Mvc.Rendering /// HtmlString Label(string expression, string labelText, object htmlAttributes); + /// + /// Returns a multi-selection HTML {select} element using the specified name of the form field, + /// list items, and HTML attributes. + /// + /// The name of the form field to return. + /// A collection of objects that are used to populate the + /// drop-down list. + /// An object that contains the HTML attributes to set for the element. + /// Alternatively, an instance containing the HTML attributes. + /// + /// An HTML {select} element with an {option} subelement for each item in the list. + HtmlString ListBox(string name, IEnumerable selectList, object htmlAttributes); + /// /// Gets the full HTML field name for the given expression . /// diff --git a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs index e65c1a7679..96dca5bec2 100644 --- a/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs +++ b/src/Microsoft.AspNet.Mvc.Core/Rendering/IHtmlHelperOfT.cs @@ -175,6 +175,24 @@ namespace Microsoft.AspNet.Mvc.Rendering string labelText, object htmlAttributes); + /// + /// Returns a multi-selection HTML {select} element for the object that is represented by the specified + /// expression using the specified list items and HTML attributes. + /// + /// The type of the property. + /// An expression that identifies the object that contains the properties to + /// display. + /// A collection of objects that are used to populate the + /// drop-down list. + /// An object that contains the HTML attributes to set for the element. + /// Alternatively, an instance containing the HTML attributes. + /// + /// An HTML {select} element with an {option} subelement for each item in the list. + HtmlString ListBoxFor( + [NotNull] Expression> expression, + IEnumerable selectList, + object htmlAttributes); + /// /// Gets the full HTML field name for the given . ///