Add `ListBox()` and `ListBoxFor()` helper methods

- copy from legacy MVC
- get `ListBox[For]()` methods in correct places and working
 - also usual stuff: `var`, `[NotNull]`, remove `IDictionary<,>` overloads
 - `ListBoxHelper()` -> `GenerateListBox`
 - already had all the bits needed in `GenerateSelect()`
- special-case `null` or empty `name` in GenerateSelect()
 - ensure `ArgumentException` has correct parameter name
 - lower-level problem affected `CheckBox()` as well
- use `ListBox()` and `ListBoxFor()` in MVC sample
 - use `ListBox()` in an editor template
This commit is contained in:
dougbu 2014-04-28 21:31:01 -07:00
parent 7a8dc36553
commit 67b33868a3
10 changed files with 130 additions and 5 deletions

View File

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

View File

@ -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<string>();
ParentsAges = new List<int>();
}
[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<string> OwnedAddresses { get; private set; }
// This does not bind correctly. Only gets highest value.
public List<int> ParentsAges { get; private set; }
}
}

View File

@ -24,6 +24,7 @@
<Content Include="Content\bootstrap.min.css" />
<Content Include="Project.json" />
<Content Include="Views\Home\Create.cshtml" />
<Content Include="Views\Home\EditorTemplates\IEnumerable`1.cshtml" />
<Content Include="Views\Home\Test.cshtml" />
<Content Include="Views\Home\ValidationSummary.cshtml" />
<Content Include="Views\Link\Details.cshtml" />

View File

@ -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 @@
<table>
<tr>
<td>
<label class="control-label col-md-2">Model.Name</label>
<label class="control-label col-md-2" for="Name">Model.Name</label>
</td>
<td>
@Html.TextBox("Name")
@ -56,7 +56,7 @@
</tr>
<tr>
<td>
<label class="control-label col-md-2">Model.Address</label>
<label class="control-label col-md-2" for="Address">Model.Address</label>
</td>
<td>
@Html.DropDownList("Address", "Select an Address")
@ -64,7 +64,7 @@
</tr>
<tr>
<td>
<label class="control-label col-md-2">Model.Age</label>
<label class="control-label col-md-2" for="Age">Model.Age</label>
</td>
<td>
@Html.DropDownListFor(model => model.Age, (IEnumerable<SelectListItem>)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" })
</td>
</tr>
<tr>
<td>
<label class="control-label col-md-2" for="OwnedAddresses">Model.OwnedAddresses</label>
</td>
<td>
@Html.Editor("OwnedAddresses")
</td>
</tr>
<tr>
<td>
<label class="control-label col-md-2" for="ParentsAges">Model.ParentsAges</label>
</td>
<td>
@Html.ListBoxFor(model => model.ParentsAges, (IEnumerable<SelectListItem>)ViewBag.Ages, htmlAttributes: new { @class = "form-control" })
</td>
</tr>
<tr>
<td>
<input type="submit" value="Save" class="btn btn-default" style="margin-left: 10px" />

View File

@ -0,0 +1,4 @@
@using System.Collections.Generic
@model IEnumerable<string>
@Html.ListBox(null, (IEnumerable<SelectListItem>)ViewBag.Address)

View File

@ -332,6 +332,12 @@ namespace Microsoft.AspNet.Mvc.Rendering
htmlAttributes);
}
/// <inheritdoc />
public HtmlString ListBox(string name, IEnumerable<SelectListItem> selectList, object htmlAttributes)
{
return GenerateListBox(metadata: null, name: name, selectList: selectList, htmlAttributes: htmlAttributes);
}
/// <inheritdoc />
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<SelectListItem> 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;
}

View File

@ -170,6 +170,18 @@ namespace Microsoft.AspNet.Mvc.Rendering
return GenerateLabel(metadata, ExpressionHelper.GetExpressionText(expression), labelText, htmlAttributes);
}
/// <inheritdoc />
public HtmlString ListBoxFor<TProperty>(
[NotNull] Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> selectList,
object htmlAttributes)
{
var metadata = GetModelMetadata(expression);
var name = ExpressionHelper.GetExpressionText(expression);
return GenerateListBox(metadata, name, selectList, htmlAttributes);
}
/// <inheritdoc />
public HtmlString NameFor<TProperty>([NotNull] Expression<Func<TModel, TProperty>> expression)
{

View File

@ -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<SelectListItem> selectList)
{
return htmlHelper.ListBox(name, selectList, htmlAttributes: null);
}
public static HtmlString ListBoxFor<TModel, TProperty>(
[NotNull] this IHtmlHelper<TModel> htmlHelper,
[NotNull] Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> selectList)
{
return htmlHelper.ListBoxFor(expression, selectList, htmlAttributes: null);
}
}
}

View File

@ -294,6 +294,19 @@ namespace Microsoft.AspNet.Mvc.Rendering
/// </returns>
HtmlString Label(string expression, string labelText, object htmlAttributes);
/// <summary>
/// Returns a multi-selection HTML {select} element using the specified name of the form field,
/// list items, and HTML attributes.
/// </summary>
/// <param name="name">The name of the form field to return.</param>
/// <param name="selectList">A collection of <see href="SelectListItem"/> objects that are used to populate the
/// drop-down list.</param>
/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.
/// Alternatively, an <see cref="IDictionary{string, object}"/> instance containing the HTML attributes.
/// </param>
/// <returns>An HTML {select} element with an {option} subelement for each item in the list.</returns>
HtmlString ListBox(string name, IEnumerable<SelectListItem> selectList, object htmlAttributes);
/// <summary>
/// Gets the full HTML field name for the given expression <paramref name="name"/>.
/// </summary>

View File

@ -175,6 +175,24 @@ namespace Microsoft.AspNet.Mvc.Rendering
string labelText,
object htmlAttributes);
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="TProperty">The type of the property.</typeparam>
/// <param name="expression">An expression that identifies the object that contains the properties to
/// display.</param>
/// <param name="selectList">A collection of <see href="SelectListItem"/> objects that are used to populate the
/// drop-down list.</param>
/// <param name="htmlAttributes">An object that contains the HTML attributes to set for the element.
/// Alternatively, an <see cref="IDictionary{string, object}"/> instance containing the HTML attributes.
/// </param>
/// <returns>An HTML {select} element with an {option} subelement for each item in the list.</returns>
HtmlString ListBoxFor<TProperty>(
[NotNull] Expression<Func<TModel, TProperty>> expression,
IEnumerable<SelectListItem> selectList,
object htmlAttributes);
/// <summary>
/// Gets the full HTML field name for the given <paramref name="expression"/>.
/// </summary>